go port
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
package tron
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
|
||||
"code.luxferre.top/luxferre/kisstron/keccak"
|
||||
)
|
||||
|
||||
// zeroOwner is the owner address used for read-only TRC20 calls, matching
|
||||
// tronpy's Contract default (0x41 followed by 20 zero bytes).
|
||||
const zeroOwner = "410000000000000000000000000000000000000000"
|
||||
|
||||
// selector returns the 4-byte ABI function selector (as hex) for a signature.
|
||||
func selector(signature string) string {
|
||||
h := keccak.Sum256([]byte(signature))
|
||||
return hex.EncodeToString(h[:4])
|
||||
}
|
||||
|
||||
var (
|
||||
selectorDecimals = selector("decimals()")
|
||||
selectorBalanceOf = selector("balanceOf(address)")
|
||||
selectorTransfer = selector("transfer(address,uint256)")
|
||||
)
|
||||
|
||||
// Contract is a minimal handle to a TRC20 contract. Only the three TRC20 calls
|
||||
// used by kisstron (decimals, balanceOf, transfer) are supported, so no ABI is
|
||||
// fetched or parsed.
|
||||
type Contract struct {
|
||||
Address string
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Decimals returns the token's decimal precision.
|
||||
func (c *Contract) Decimals() (int, error) {
|
||||
res, err := c.client.triggerConstantContract(zeroOwner, c.Address, "decimals()", "")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
v, err := decodeUint256(res)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(v.Int64()), nil
|
||||
}
|
||||
|
||||
// BalanceOf returns the raw token balance of addr.
|
||||
func (c *Contract) BalanceOf(addr string) (*big.Int, error) {
|
||||
param, err := encodeAddressParam(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := c.client.triggerConstantContract(zeroOwner, c.Address, "balanceOf(address)", param)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decodeUint256(res)
|
||||
}
|
||||
|
||||
// Transfer builds a signed-ready TRC20 transfer transaction builder.
|
||||
func (c *Contract) Transfer(from, to string, amount *big.Int) (*TransactionBuilder, error) {
|
||||
fromAddr, err := encodeAddressParam(to)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fromHex, err := ToHexAddress(from)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
contractHex, err := ToHexAddress(c.Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := selectorTransfer + fromAddr + encodeUint256(amount)
|
||||
return c.client.newTransactionBuilder("TriggerSmartContract", TriggerValue{
|
||||
OwnerAddress: fromHex,
|
||||
ContractAddress: contractHex,
|
||||
Data: data,
|
||||
}), nil
|
||||
}
|
||||
|
||||
// encodeAddressParam left-pads a TVM address to a 32-byte ABI word.
|
||||
func encodeAddressParam(addr string) (string, error) {
|
||||
tvm, err := ToTvmAddress(addr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
word := make([]byte, 32)
|
||||
copy(word[32-len(tvm):], tvm)
|
||||
return hex.EncodeToString(word), nil
|
||||
}
|
||||
|
||||
// encodeUint256 encodes a non-negative integer as a 32-byte ABI word.
|
||||
func encodeUint256(v *big.Int) string {
|
||||
word := make([]byte, 32)
|
||||
if v != nil {
|
||||
v.FillBytes(word)
|
||||
}
|
||||
return hex.EncodeToString(word)
|
||||
}
|
||||
|
||||
// decodeUint256 decodes a 32-byte ABI word (hex) into a big integer.
|
||||
func decodeUint256(hexStr string) (*big.Int, error) {
|
||||
b, err := hex.DecodeString(hexStr)
|
||||
if err != nil {
|
||||
return nil, ErrTvm
|
||||
}
|
||||
return new(big.Int).SetBytes(b), nil
|
||||
}
|
||||
Reference in New Issue
Block a user