SafeMath โค๊ดอันตราย สำหรับสาย Copy

Samret Wajanasathian
2 min readMay 26, 2020

ผมได้อ่าน code ของคนเขียน smart contract หลาย ๆ คน ส่วนใหญ่จะ copy library ที่เรียกว่า SafeMath โดยมี Code แบบนี้

library SafeMath {function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b,”MUL ERROR”);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn’t hold
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a,”Sub Error”);
return a — b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a,”add ERROR”);
return c;
}
}

ดูผ่านๆ ก็อาจจะไม่มีอะไร แต่มันมีตรง การคุณ และ การหารนี้หล่ะ เพื่อทำงานกับ Token เพราะ ในระบบของ Solidity นั้น มันไม่มี ทศนิยม เหรียญแต่ละเหรียญ ก็จะใช้การนิยามว่ามีทศนิยมกี่ตำแหน่ง แล้วก็แปลงมันซะ เช่น มี 10 ตำแหน่ง
1 token ก็จะได้ค่า 10000000000 หรือ ถ้ามี 2 ตำแหน่ง ก็จะได้ 1 token มีค่าเท่ากับ 100 อะไรแบบนี้ (ถ้าเอามาคำนวนปกติก็ไม่เป็นไร แต่จะคำนวน แบบทศนิยมก็ต้องใช้วิธีนี้อยู่ดี)

ปัญหาจะตามมาก็ต่อเมื่อ เราเอา เหรียญเหล่านี้มาคำนวนทางคณิตศาสตร์แล้วไม่ระวัง ยกตัวอย่าง ถ้าในระบบเรามี ทศนิยม 2 ตำแหน่ง

1 จะเท่ากับ 100 หรือถ้าเป็น 1.45 ก็จะได้ค่า 145 อะไรแบบนี้
เพราะงั้น ถ้าเราเอามาคุณ กับ เหรียญทีมีอยู่ ยกตัวอย่างเช่น ดอกเบี้ย 1.5 เท่า
มันจะกลายเป็น 100 x 150 (1 token และ คุณด้วย 1.5 เท่า) ผลลัพธ์ที่ออกมาจะได้ 15000 หรือ กลายเป็น 150 token ทันที

สิ่งที่เราจะต้องแก้ก็คือ เราต้องมีการหารทิ้งเท่าจำนวน ทศนิยม เช่นกรณีนี้คือ
ทศนิยม 2 ตำแหน่ง หรือ คือ 100 ก็จะได้เป็น (100 x 150) 100 อีกที

เพราะฉะนั้น code ของการ คุณ จึงควรเป็น

function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = (a * b)/ (10 ** decimal);
require(c / a == b,”MUL ERROR”);
return c;
}

และในทางกลับการ การ หาร ก็มีปัญหาเช่นกัน
เพราะยกตัวอย่างคุณมี 10 Token หรือ ก็คือ 1000 ต้องการ หารด้วย 5 หรือก็คือ 500 ซึ่งผลลัพธ์ต้องออกมาเป็น 2 หรือเท่ากับ 200 แต่การหารจะได้ค่าเป็น 2 เฉย ๆ ซึ่งเท่ากับ 0.02 แทน ไอ้ครั้นบอกว่างั้นก็หาร 5 ไปเลยซิ ได้ 200 ไง งั้น ถ้าผมจะหาร 0.05 จะเอาค่าที่ไหนหล่ะ เพราะ 5 มันต้องหมายถึง 0.05 นะครับหาร 5 เลยต้องเป็น 500 แทน

เพราะฉะนั้น code ของการหาร ที่ถูกต้องเลยต้องเป็น

function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = (a / b) * (10 ** decimal);
// assert(a == b * c + a % b); // There is no case in which this doesn’t hold
return c;
}

สรุปก็คือ การคูณ ต้องหารออกด้วย 10 ยกกำลัง จำนวนทศนิยม และ การหาร ก็ต้อง คูณเพิ่มเข้าด้วย 10 ยกกำลัง จำนวนทศนิยมครับ

--

--

Samret Wajanasathian

จงทำตัวเล็ก แต่เงาใหญ่