github.com/s7techlab/cckit@v0.10.5/examples/erc20/erc20_ops.go (about) 1 package erc20 2 3 import ( 4 "github.com/pkg/errors" 5 "github.com/s7techlab/cckit/identity" 6 r "github.com/s7techlab/cckit/router" 7 ) 8 9 const ( 10 BalancePrefix = `BALANCE` 11 AllowancePrefix = `APPROVE` 12 ) 13 14 var ( 15 ErrNotEnoughFunds = errors.New(`not enough funds`) 16 ErrForbiddenToTransferToSameAccount = errors.New(`forbidden to transfer to same account`) 17 ErrSpenderNotHaveAllowance = errors.New(`spender not have allowance for amount`) 18 ) 19 20 type ( 21 Transfer struct { 22 From identity.Id 23 To identity.Id 24 Amount int 25 } 26 27 Approve struct { 28 From identity.Id 29 Spender identity.Id 30 Amount int 31 } 32 ) 33 34 func querySymbol(c r.Context) (interface{}, error) { 35 return c.State().Get(SymbolKey) 36 } 37 38 func queryName(c r.Context) (interface{}, error) { 39 return c.State().Get(NameKey) 40 } 41 42 func queryTotalSupply(c r.Context) (interface{}, error) { 43 return c.State().Get(TotalSupplyKey) 44 } 45 46 func queryBalanceOf(c r.Context) (interface{}, error) { 47 return getBalance(c, c.ArgString(`mspId`), c.ArgString(`certId`)) 48 } 49 50 func invokeTransfer(c r.Context) (interface{}, error) { 51 // transfer target 52 toMspId := c.ParamString(`toMspId`) 53 toCertId := c.ParamString(`toCertId`) 54 55 //transfer amount 56 amount := c.ParamInt(`amount`) 57 58 // get information about tx creator 59 invoker, err := identity.FromStub(c.Stub()) 60 if err != nil { 61 return nil, err 62 } 63 64 // Disallow to transfer token to same account 65 if invoker.GetMSPIdentifier() == toMspId && invoker.GetID() == toCertId { 66 return nil, ErrForbiddenToTransferToSameAccount 67 } 68 69 // get information about invoker balance from state 70 invokerBalance, err := getBalance(c, invoker.GetMSPIdentifier(), invoker.GetID()) 71 if err != nil { 72 return nil, err 73 } 74 75 // Check the funds sufficiency 76 if invokerBalance-amount < 0 { 77 return nil, ErrNotEnoughFunds 78 } 79 80 // Get information about recipient balance from state 81 recipientBalance, err := getBalance(c, toMspId, toCertId) 82 if err != nil { 83 return nil, err 84 } 85 86 // Update payer and recipient balance 87 if err = setBalance(c, invoker.GetMSPIdentifier(), invoker.GetID(), invokerBalance-amount); err != nil { 88 return nil, err 89 } 90 91 if err = setBalance(c, toMspId, toCertId, recipientBalance+amount); err != nil { 92 return nil, err 93 } 94 95 // Trigger event with name "transfer" and payload - serialized to json Transfer structure 96 if err = c.SetEvent(`transfer`, &Transfer{ 97 From: identity.Id{ 98 MSP: invoker.GetMSPIdentifier(), 99 Cert: invoker.GetID(), 100 }, 101 To: identity.Id{ 102 MSP: toMspId, 103 Cert: toCertId, 104 }, 105 Amount: amount, 106 }); err != nil { 107 return nil, err 108 } 109 110 // return current invoker balance 111 return invokerBalance - amount, nil 112 } 113 114 func queryAllowance(c r.Context) (interface{}, error) { 115 return getAllowance(c, 116 c.ParamString(`ownerMspId`), c.ParamString(`ownerCertId`), 117 c.ParamString(`spenderMspId`), c.ParamString(`spenderCertId`)) 118 } 119 120 // Allow spender, identified by mspId add to spend amount 121 func invokeApprove(c r.Context) (interface{}, error) { 122 spenderMspId := c.ParamString(`spenderMspId`) 123 spenderCertId := c.ParamString(`spenderCertId`) 124 amount := c.ParamInt(`amount`) 125 126 invoker, err := identity.FromStub(c.Stub()) 127 if err != nil { 128 return nil, err 129 } 130 131 if err = setAllowance(c, invoker.GetMSPIdentifier(), invoker.GetID(), spenderMspId, spenderCertId, amount); err != nil { 132 return nil, err 133 } 134 135 if err = c.SetEvent(`approve`, &Approve{ 136 From: identity.Id{ 137 MSP: invoker.GetMSPIdentifier(), 138 Cert: invoker.GetID(), 139 }, 140 Spender: identity.Id{ 141 MSP: spenderMspId, 142 Cert: spenderCertId, 143 }, 144 Amount: amount, 145 }); err != nil { 146 return nil, err 147 } 148 149 return true, nil 150 } 151 152 // Transfer amount from wallet1 (from) to wallet2 (to) 153 // wherein the initiator of the operation (tx creator) is not the owner of the wallet1 154 // Tx creator has to have approval from owner of wallet1 for amount greater than or equal to transfer amount 155 func invokeTransferFrom(c r.Context) (interface{}, error) { 156 157 fromMspId := c.ParamString(`fromMspId`) 158 fromCertId := c.ParamString(`fromCertId`) 159 toMspId := c.ParamString(`toMspId`) 160 toCertId := c.ParamString(`toCertId`) 161 amount := c.ParamInt(`amount`) 162 163 invoker, err := identity.FromStub(c.Stub()) 164 if err != nil { 165 return nil, err 166 } 167 168 // check method invoker has allowances 169 allowance, err := getAllowance(c, fromMspId, fromCertId, invoker.GetMSPIdentifier(), invoker.GetID()) 170 if err != nil { 171 return nil, err 172 } 173 174 // transfer amount must be less or equal allowance 175 if allowance < amount { 176 return nil, ErrSpenderNotHaveAllowance 177 } 178 179 // current payer balance 180 balance, err := getBalance(c, fromMspId, fromCertId) 181 if err != nil { 182 return nil, err 183 } 184 185 // payer balance must be greater or equal amount 186 if balance-amount < 0 { 187 return nil, ErrNotEnoughFunds 188 } 189 190 // current recipient balance 191 recipientBalance, err := getBalance(c, toMspId, toCertId) 192 if err != nil { 193 return nil, err 194 } 195 196 // decrease payer balance 197 if err = setBalance(c, fromMspId, fromCertId, balance-amount); err != nil { 198 return nil, err 199 } 200 201 // increase recipient balance 202 if err = setBalance(c, toMspId, toCertId, recipientBalance+amount); err != nil { 203 return nil, err 204 } 205 206 // decrease invoker allowance 207 if err = setAllowance(c, fromMspId, fromCertId, invoker.GetMSPIdentifier(), invoker.GetID(), allowance-amount); err != nil { 208 return nil, err 209 } 210 211 if err = c.Event().Set(`transfer`, &Transfer{ 212 From: identity.Id{ 213 MSP: fromMspId, 214 Cert: fromCertId, 215 }, 216 To: identity.Id{ 217 MSP: toMspId, 218 Cert: toCertId, 219 }, 220 Amount: amount, 221 }); err != nil { 222 return nil, err 223 } 224 225 // return current invoker balance 226 return balance - amount, nil 227 } 228 229 // === internal functions, not "public" chaincode functions 230 231 // setBalance puts balance value to state 232 func balanceKey(ownerMspId, ownerCertId string) []string { 233 return []string{BalancePrefix, ownerMspId, ownerCertId} 234 } 235 236 func allowanceKey(ownerMspId, ownerCertId, spenderMspId, spenderCertId string) []string { 237 return []string{AllowancePrefix, ownerMspId, ownerCertId, spenderMspId, spenderCertId} 238 } 239 240 func getBalance(c r.Context, mspId, certId string) (int, error) { 241 return c.State().GetInt(balanceKey(mspId, certId), 0) 242 } 243 244 // setBalance puts balance value to state 245 func setBalance(c r.Context, mspId, certId string, balance int) error { 246 return c.State().Put(balanceKey(mspId, certId), balance) 247 } 248 249 // getAllowance gets amount of token allowed by wallet owner to spend by spender 250 func getAllowance(c r.Context, ownerMspId, ownerCertId, spenderMspId, spenderCertId string) (int, error) { 251 return c.State().GetInt(allowanceKey(ownerMspId, ownerCertId, spenderMspId, spenderCertId), 0) 252 } 253 254 func setAllowance(c r.Context, ownerMspId, ownerCertId, spenderMspId, spenderCertId string, amount int) error { 255 return c.State().Put(allowanceKey(ownerMspId, ownerCertId, spenderMspId, spenderCertId), amount) 256 }