paysplit.aes
1 payable contract PaymentSplitter = 2 record state = 3 { owner: address, 4 recipientConditions: map(address, int), // map of recipients with percentage to receive (value between 1 and 100) 5 totalAmountSplitted: int } 6 7 // CONTRACT EVENTS 8 datatype event = AddingInitialRecipients() 9 | RecipientAdded(indexed address, indexed int) 10 | AddressUpdated(indexed address, indexed address) 11 | UpdatingAllRecipients() 12 | PaymentReceivedAndSplitted(indexed address, indexed int, indexed int) 13 14 // CONSTRUCTOR 15 entrypoint init(recipientConditions': map(address, int)) : state = 16 require(sumWeights(recipientConditions') == 100, "sum of weights needs to be 100") 17 Chain.event(AddingInitialRecipients) 18 { owner = Call.caller, 19 recipientConditions = recipientConditions', 20 totalAmountSplitted = 0} 21 22 // READ ONLY FUNCTIONS 23 24 entrypoint getOwner() : address = 25 state.owner 26 27 entrypoint getRecipientsCount() : int = 28 Map.size(state.recipientConditions) 29 30 entrypoint isRecipient(who': address) : bool = 31 Map.member(who', state.recipientConditions) 32 33 entrypoint getWeight(who': address) : int = 34 Map.lookup_default(who', state.recipientConditions, 0) 35 36 entrypoint getTotalAmountSplitted() : int = 37 state.totalAmountSplitted 38 39 // PAY-AND-SPLIT FUNCTION 40 stateful payable entrypoint payAndSplit() = 41 require(Contract.balance > 0, "contract didn't receive any payment") 42 let recipientConditions: list(address * int) = Map.to_list(state.recipientConditions) 43 put(state{totalAmountSplitted = Contract.balance + state.totalAmountSplitted}) 44 split(recipientConditions, Contract.balance) 45 Chain.event(PaymentReceivedAndSplitted(Call.caller, Call.value, Contract.balance)) 46 47 // STATEFUL FUNCTIONS 48 49 stateful entrypoint transferOwnership(newOwner': address) = 50 onlyOwner() 51 put(state{owner = newOwner'}) 52 53 stateful entrypoint updateAddress(oldAddress': address, newAddress': address) = 54 onlyOwnerOrRecipient(oldAddress') 55 let weight: int = state.recipientConditions[oldAddress'] 56 put(state{recipientConditions @ rc = Map.delete(oldAddress', rc)}) // remove old address 57 put(state{recipientConditions[newAddress'] = weight}) // add new address 58 Chain.event(AddressUpdated(oldAddress', newAddress')) 59 60 stateful entrypoint updateRecipientConditions(recipients': map(address, int)) = 61 onlyOwner() 62 Chain.event(UpdatingAllRecipients) 63 require(sumWeights(recipients') == 100, "sum of weights needs to be 100") 64 put(state{recipientConditions = recipients'}) 65 fireRecipientAddedEvents(Map.to_list(state.recipientConditions)) 66 67 // PRIVATE FUNCTIONS 68 69 function onlyOwner() = 70 require(Call.caller == state.owner, "caller must be the owner") 71 72 function onlyOwnerOrRecipient(recipient': address) = 73 require(Call.caller == state.owner || Call.caller == recipient', "caller must be the owner or the recipient") 74 75 function sumWeights(recipients': map(address, int)) : int = 76 let recipientList: list(address * int) = Map.to_list(recipients') 77 let intList: list(int) = map(pair_second, recipientList) 78 sum(intList, (x) => x) 79 80 function fireRecipientAddedEvents(recipientConditions': list(address * int)) = 81 switch(recipientConditions') 82 [] => () 83 (recipient, weight) :: l' => 84 Chain.event(RecipientAdded(recipient, weight)) 85 86 stateful function split(recipientConditions': list(address * int), totalValue: int) = 87 switch(recipientConditions') 88 [] => () 89 (recipient, weight) :: l' => 90 Chain.spend(recipient, totalValue / 100 * weight) 91 split(l', totalValue) 92 93 // GENERIC HELPER FUNCTIONS 94 95 function map(f : 'a => 'b, l : list('a)) : list('b) = 96 switch(l) 97 [] => [] 98 e :: l' => f(e) :: map(f, l') 99 100 function foldr(f : (('a, 'b) => 'b), z: 'b, l : list('a)) : 'b = 101 switch(l) 102 [] => z 103 e :: l' => f(e, foldr(f, z, l')) 104 105 function sum(l : list('a), f : 'a => int) : int = 106 foldr((x, y) => x + y, 0, map(f, l)) 107 108 function pair_second(tuple) = 109 switch(tuple) 110 (_, e) => e