Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions examples/gno.land/p/demo/tokens/grc20/tellers_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ func TestTeller(t *testing.T) {
checkBalances(1000, 0, 100)
checkAllowances(300, 0, 0, 0, 0, 0)

urequire.Error(t, ledger.SpendAllowance(alice, bob, 2000000))
urequire.Error(t, ledger.spendAllowance(alice, bob, 2000000))
checkBalances(1000, 0, 100)
checkAllowances(300, 0, 0, 0, 0, 0)

urequire.NoError(t, ledger.SpendAllowance(alice, bob, 100))
urequire.NoError(t, ledger.spendAllowance(alice, bob, 100))
checkBalances(1000, 0, 100)
checkAllowances(200, 0, 0, 0, 0, 0)
}
Expand Down
83 changes: 40 additions & 43 deletions examples/gno.land/p/demo/tokens/grc20/token.gno
Original file line number Diff line number Diff line change
Expand Up @@ -79,35 +79,6 @@ func (tok Token) RenderHome() string {
return str
}

// SpendAllowance decreases the allowance of the specified owner and spender.
func (led *PrivateLedger) SpendAllowance(owner, spender address, amount int64) error {
if !owner.IsValid() {
return ErrInvalidAddress
}
if !spender.IsValid() {
return ErrInvalidAddress
}
if amount < 0 {
return ErrInvalidAmount
}

currentAllowance := led.allowance(owner, spender)
if currentAllowance < amount {
return ErrInsufficientAllowance
}

key := allowanceKey(owner, spender)
newAllowance := overflow.Sub64p(currentAllowance, amount)

if newAllowance == 0 {
led.allowances.Remove(key)
} else {
led.allowances.Set(key, newAllowance)
}

return nil
}

// Transfer transfers tokens from the specified from address to the specified to address.
func (led *PrivateLedger) Transfer(from, to address, amount int64) error {
if !from.IsValid() {
Expand Down Expand Up @@ -162,30 +133,25 @@ func (led *PrivateLedger) TransferFrom(owner, spender, to address, amount int64)
if amount < 0 {
return ErrInvalidAmount
}

if !owner.IsValid() || !to.IsValid() {
return ErrInvalidAddress
}

if led.balanceOf(owner) < amount {
return ErrInsufficientBalance
}

// allowance must be sufficient
currentAllowance := led.allowance(owner, spender)
if currentAllowance < amount {
return ErrInsufficientAllowance
// The check above guarantees that Transfer will succeed, ensuring
// atomicity for the subsequent operations.
if err := led.spendAllowance(owner, spender, amount); err != nil {
return err
}

if err := led.Transfer(owner, to, amount); err != nil {
return err
}

// decrease the allowance only when transfer is successful
key := allowanceKey(owner, spender)
newAllowance := overflow.Sub64p(currentAllowance, amount)

if newAllowance == 0 {
led.allowances.Remove(key)
} else {
led.allowances.Set(key, newAllowance)
}

return nil
}

Expand Down Expand Up @@ -276,6 +242,37 @@ func (led *PrivateLedger) Burn(addr address, amount int64) error {
return nil
}

// spendAllowance decreases the allowance of the specified owner and spender.
func (led *PrivateLedger) spendAllowance(owner, spender address, amount int64) error {
if !owner.IsValid() || !spender.IsValid() {
return ErrInvalidAddress
}

if amount < 0 {
return ErrInvalidAmount
}
// do nothing
if amount == 0 {
return nil
}

currentAllowance := led.allowance(owner, spender)
if currentAllowance < amount {
return ErrInsufficientAllowance
}

key := allowanceKey(owner, spender)
newAllowance := overflow.Sub64p(currentAllowance, amount)

if newAllowance == 0 {
led.allowances.Remove(key)
} else {
led.allowances.Set(key, newAllowance)
}

return nil
}

// hasAddr checks if the specified address is a known account in the ledger.
func (led PrivateLedger) hasAddr(addr address) bool {
return led.balances.Has(addr.String())
Expand Down
14 changes: 12 additions & 2 deletions examples/gno.land/p/demo/tokens/grc20/token_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ func TestToken(t *testing.T) {
checkBalances(1000, 0, 100)
checkAllowances(300, 0, 0, 0, 0, 0)

urequire.Error(t, adm.SpendAllowance(alice, bob, 2000000))
urequire.Error(t, adm.spendAllowance(alice, bob, 2000000))
checkBalances(1000, 0, 100)
checkAllowances(300, 0, 0, 0, 0, 0)

urequire.NoError(t, adm.SpendAllowance(alice, bob, 100))
urequire.NoError(t, adm.spendAllowance(alice, bob, 100))
checkBalances(1000, 0, 100)
checkAllowances(200, 0, 0, 0, 0, 0)
}
Expand All @@ -98,6 +98,7 @@ func TestTransferFromAtomicity(t *testing.T) {
spender = testutils.TestAddress("spender")

invalidRecipient = address("")
recipient = testutils.TestAddress("to")
)

token, admin := NewToken("Test", "TEST", 6)
Expand All @@ -121,6 +122,15 @@ func TestTransferFromAtomicity(t *testing.T) {
remainingAllowance := token.Allowance(owner, spender)
uassert.Equal(t, remainingAllowance, initialAllowance,
"allowance should not be reduced when transfer fails")

// transfer all tokens
admin.Transfer(owner, recipient, 100)
remainingBalance := token.BalanceOf(owner)
uassert.Equal(t, remainingBalance, int64(0),
"balance should be zero")

err = admin.TransferFrom(owner, spender, recipient, transferAmount)
uassert.Error(t, err, "transfer should fail due to insufficient balance")
}

func TestMintUntilOverflow(t *testing.T) {
Expand Down
Loading