import { ByteArray , BytesReader , CompactInt } from 'as-scale-codec' ; import { AccountData , AccountId , ExistenceRequirement , ResponseCodes , Storage , Utils , WithdrawReasons } from 'subsembly-core' ; import { StorageEntry , SystemStorageEntries } from '../../frame' ; import { AccountIdType , Balance , BalancesConfig , NonceType } from '../../runtime' ; enum EventTypes { Transfer = 0 , BalanceSet = 1 } export namespace BalancesStorageEntries { description storage_map export function Account ( ) { return new StorageEntry < AccountData < Balance > > ( "Balances" , "Account" ) ; } } description export class Balances { static readonly BALANCESET : u8 [ ] = [ 2 , 0 ] ; static readonly TRANSFER : u8 [ ] = [ 2 , 1 ] ; description static _setBalance ( accountId , freeBalance , reservedBalance ) { if ( BalancesConfig . existentialDeposit ( ) . unwrap ( ) > freeBalance . unwrap ( ) ) { return ResponseCodes . VALIDITY_ERROR ; } const accData = BalancesStorageEntries . Account ( ) . get ( accountId ) ; accData . setFree ( freeBalance ) ; accData . setReserved ( reservedBalance ) ; BalancesStorageEntries . Account ( ) . set ( accData , accountId ) ; Balances . _depositEvent ( EventTypes . BalanceSet , accountId . toU8a ( ) . concat ( freeBalance . toU8a ( ) ) ) ; return ResponseCodes . SUCCESS ; } description param dest param value returns static transfer ( dest , value ) { return [ ] ; } description param source param dest param value static _applyTransfer ( source , dest , value ) { const senderBalance = BalancesStorageEntries . Account ( ) . get ( source ) ; const receiverBalance = BalancesStorageEntries . Account ( ) . get ( dest ) ; if ( senderBalance . getFree ( ) . unwrap ( ) < value . unwrap ( ) ) { return ResponseCodes . INSUFFICIENT_BALANCE ; } const newFreeBalance = senderBalance . getFree ( ) . unwrap ( ) - value . unwrap ( ) ; if ( newFreeBalance < BalancesConfig . existentialDeposit ( ) . unwrap ( ) ) { return ResponseCodes . VALIDITY_ERROR ; } senderBalance . setFree ( instantiate < Balance > ( newFreeBalance ) ) ; receiverBalance . setFree ( instantiate < Balance > ( receiverBalance . getFree ( ) . unwrap ( ) + value . unwrap ( ) ) ) ; BalancesStorageEntries . Account ( ) . set ( senderBalance , source ) ; BalancesStorageEntries . Account ( ) . set ( receiverBalance , dest ) ; const senderInfo = SystemStorageEntries . Account ( ) . get ( source ) ; const receiverInfo = SystemStorageEntries . Account ( ) . get ( dest ) ; senderInfo . setData ( senderBalance ) ; receiverInfo . setData ( receiverBalance ) ; senderInfo . setNonce ( instantiate < NonceType > ( senderInfo . nonce . unwrap ( ) + 1 ) ) ; SystemStorageEntries . Account ( ) . set ( senderInfo , source ) ; SystemStorageEntries . Account ( ) . set ( receiverInfo , dest ) ; Balances . _depositEvent ( EventTypes . Transfer , source . toU8a ( ) . concat ( dest . toU8a ( ) ) . concat ( value . toU8a ( ) ) ) ; return ResponseCodes . SUCCESS ; } description param name param args static _depositEvent ( type , args ) { switch ( type ) { case EventTypes . Transfer : { Balances . _addEventInStorage ( this . TRANSFER , args ) ; } case EventTypes . BalanceSet : { Balances . _addEventInStorage ( this . BALANCESET , args ) ; } default : return ; } } description param who param amount static _validateTransaction ( who , amount ) { const currentBalance = BalancesStorageEntries . Account ( ) . get ( who ) ; if ( BalancesConfig . existentialDeposit ( ) . unwrap ( ) > currentBalance . getFree ( ) . unwrap ( ) ) { return ResponseCodes . VALIDITY_ERROR ; } const newBalance = currentBalance . getFree ( ) . unwrap ( ) - amount . unwrap ( ) ; if ( newBalance < amount . unwrap ( ) ) { return ResponseCodes . INSUFFICIENT_BALANCE ; } return ResponseCodes . SUCCESS ; } description param who param fee param reason param existenceRequirement static _withdraw ( who , fee , reason , existenceRequirement ) { if ( reason == WithdrawReasons . TRANSACTION_PAYMENT ) { const balance = BalancesStorageEntries . Account ( ) . get ( who ) . getFree ( ) ; if ( Utils . areArraysEqual ( ResponseCodes . SUCCESS , this . _validateTransaction ( who , fee ) ) ) { BalancesStorageEntries . Account ( ) . set ( instantiate < Balance > ( balance . unwrap ( ) - fee . unwrap ( ) ) , who ) ; return ; } } } description param event static _addEventInStorage ( event , args ) { /**
         * TO-DO: Make event deposit dynamic
         * Currently it's hard-coded for two types of events:
         * ExtrinsicSuccess and ExtrinsicFailed
         */ const events = Storage . get ( Utils . getHashedKey ( "Balances" , "Events" , null ) ) ; const eventsRaw = events . isSome ( ) ? ( < ByteArray > events . unwrap ( ) ) . unwrap ( ) : [ 0 ] ; const bytesReader = new BytesReader ( eventsRaw ) ; const len = < i32 > bytesReader . readInto < CompactInt > ( ) . unwrap ( ) ; const newEvents = new CompactInt ( len + 1 ) . toU8a ( ) . concat ( bytesReader . getLeftoverBytes ( ) ) . concat ( event ) . concat ( args ) ; Storage . set ( Utils . getHashedKey ( "Balances" , "Events" , null ) , newEvents ) ; } }