Box aepp Defi-v2 ABC Token Mining plan Changes and contract codes

Hello, everyone
On October 26, our Chinese community opened its first cosmic meeting. Completed the Box AEPP ABC token Version of V2 mining plan. The changes to the new contract are as follows:

ABC’s first universe conference ended successfully with 11 attendees
Discussion question ABC V2 mining rules discussion and draft determination
Conclusion: 11 agree and 0 disagree
The total quantity of ABC is reduced to 5000W, and the proportion of customers holding positions has been reduced. The mining time of the old contract is around 10am on October 27th, 2020.
AE used in V2 version ABC mining can be accessed flexibly.
The mining calculation is based on the height of the pledged block - the height of the easy to generate block * the income of each block.
It is provisionally assumed that 1w AE produced in a day is approximately equal to 1 ABC.
The old contract retained the unlock port, which could be automatically retrieved when it expired

The following is the V2 contract code, please audit code vulnerabilities.

contract FungibleTokenInterface =
  entrypoint total_supply                  : ()                      => int
  entrypoint balance                       : (address)               => option(int)
  stateful entrypoint transfer             : (address, int)          => unit

include "Option.aes"
include "List.aes"

payable contract ABCLockContractV2 =

  record account = {
      account: address,
      count: int,
      height: int}

  record state = {
    token: FungibleTokenInterface,
    accounts: map(address, account),
    all_count: int,
    decimals: int}

  stateful entrypoint init(token: FungibleTokenInterface) = {
    token = token,
    accounts = {},
    all_count = 0,
    decimals = 1000000000000000000}

  entrypoint getContractBalance() =
    Contract.balance

  entrypoint getContractAddress() =
    Contract.address

  entrypoint getCallCaller() =
    Call.caller

  entrypoint getAccounts() : map(address, account) =
    state.accounts

  entrypoint getABCoutputCount(): int =
    switch(state.token.balance(Contract.address))
      Some(balance) =>
        state.token.total_supply()-balance
      None => 0


  entrypoint getAccountInfo(addr: address) =
    Map.lookup(addr, state.accounts)


  entrypoint getAccountBalance(): int =
    switch(state.token.balance(Contract.address))
      Some(balance) => balance
      None => 0


  private function getAccount(addr: address): account =
    switch(Map.lookup(addr, state.accounts))
      Some(account) => account
      None => {account = addr,count = 0, height = 0 }

  private function isAccountExist(addr: address): bool =
    switch(Map.lookup(addr, state.accounts))
      Some(account) => true
      None => false

  payable stateful entrypoint lock() =
    if(Call.value < 1 * state.decimals)
      require(2 == 1, "AMOUNT_LOW ")
    benefitsAddress()
    let account_data = getAccount(Call.caller)
    let account = {account = Call.caller , count = account_data.count + Call.value , height = Chain.block_height}
    put(state{accounts[Call.caller] = account, all_count = state.all_count + Call.value})
    Call.value

  stateful entrypoint benefits() =
    if(!isAccountExist(Call.caller))
      abort("ACCOUNT_NOT_EXIST")
    let account_data = getAccount(Call.caller)
    if(Chain.block_height - account_data.height =< 0)
      abort("ACCOUNT_HEIGHT_0")

    if(getAccountBalance() == 0)
      abort("CONTRACTS_ACCOUNT_BALANCE_0")

    let token_number = 300000000000 * (account_data.count / state.decimals) * (Chain.block_height - account_data.height)
    if(getAccountBalance() > token_number)
      state.token.transfer(Call.caller, token_number)
      state.token.transfer(ak_2Xu6d6W4UJBWyvBVJQRHASbQHQ1vjBA7d1XUeY8SwwgzssZVHK, token_number * 10 / 100)
      state.token.transfer(ak_2MHJv6JcdcfpNvu4wRDZXWzq8QSxGbhUfhMLR7vUPzRFYsDFw6, token_number * 5 / 100)
      let account = {account = account_data.account ,count = account_data.count , height = Chain.block_height}
      put(state{accounts[Call.caller] = account})
      token_number
    else
      0

  private function benefitsAddress() =
    let account_data = getAccount(Call.caller)
    if(Chain.block_height - account_data.height > 0 && account_data.height > 0)
      let token_number = 300000000000 * (account_data.count / state.decimals) * (Chain.block_height - account_data.height)
      if(getAccountBalance() > token_number)
        state.token.transfer(Call.caller, token_number)
        state.token.transfer(ak_2Xu6d6W4UJBWyvBVJQRHASbQHQ1vjBA7d1XUeY8SwwgzssZVHK, token_number * 10 / 100)
        state.token.transfer(ak_2MHJv6JcdcfpNvu4wRDZXWzq8QSxGbhUfhMLR7vUPzRFYsDFw6, token_number * 5 / 100)

  stateful entrypoint unLock(count : int) =
    if(!isAccountExist(Call.caller))
      abort("ACCOUNT_NOT_EXIST")
    let accountData = getAccount(Call.caller)
    if(accountData.count < count)
      abort("ACCOUNT_COUNT_ERROR")
    benefitsAddress()
    let account = {account = accountData.account , count = accountData.count - count , height = Chain.block_height}
    Chain.spend(accountData.account, count)
    put(state{accounts[accountData.account] = account,all_count = state.all_count - count})
    count

Thank you for your support! :smiley: :smile:

Here are the renderings and function buttons

4 Likes

can you provide the set of automated tests so we can see most cases are working as intended?

@philipp.chain

I wrote a test program according to your suggestion, and there is also a test account in it. Could you please help to have a look? It is the HTML file inside the JS, can be directly assigned to run.

<html lang="en">
<head>
    <meta charset="utf-8">
</head>
<body>
<!-- include latest SDK version -->
<script type="text/javascript" src="https://unpkg.com/@aeternity/[email protected]/dist/aepp-sdk.browser-script.js"></script>
<script type="text/javascript">



    const sourceCodeDefiV2 = `
contract FungibleTokenInterface =
  entrypoint total_supply                  : ()                      => int
  entrypoint balance                       : (address)               => option(int)
  stateful entrypoint transfer             : (address, int)          => unit

include "Option.aes"
include "List.aes"

payable contract ABCLockContractV2 =

  record account = {
      account: address,
      count: int,
      height: int}

  record state = {
    token: FungibleTokenInterface,
    accounts: map(address, account),
    all_count: int,
    decimals: int}

  stateful entrypoint init(token: FungibleTokenInterface) = {
    token = token,
    accounts = {},
    all_count = 0,
    decimals = 1000000000000000000}

  stateful entrypoint getContractBalance() =
    Contract.balance

  stateful entrypoint getContractAddress() =
    Contract.address

  stateful entrypoint getCallCaller() =
    Call.caller

  stateful entrypoint getAccounts() : map(address, account) =
    state.accounts

  entrypoint getABCoutputCount(): int =
    switch(state.token.balance(Contract.address))
      Some(balance) =>
        state.token.total_supply()-balance
      None => 0

  private function getAccount(addr: address): account =
    switch(Map.lookup(addr, state.accounts))
      Some(account) => account
      None => {account = addr,count = 0, height = 0 }

  private function isAccountExist(addr: address): bool =
    switch(Map.lookup(addr, state.accounts))
      Some(account) => true
      None => false

  payable stateful entrypoint lock() =
    if(Call.value < 1 * state.decimals)
      require(2 == 1, "AMOUNT_LOW ")
    benefitsAddress()
    let account_data = getAccount(Call.caller)
    let account = {account = Call.caller , count = account_data.count + Call.value , height = Chain.block_height}
    put(state{accounts[Call.caller] = account, all_count = state.all_count + Call.value})
    Call.value

  stateful entrypoint benefits() =
    if(!isAccountExist(Call.caller))
      abort("ACCOUNT_NOT_EXIST")
    let account_data = getAccount(Call.caller)
    if(Chain.block_height - account_data.height =< 0)
      abort("ACCOUNT_HEIGHT_0")
    let token_number = 300000000000 * (account_data.count / state.decimals) * (Chain.block_height - account_data.height)
    state.token.transfer(Call.caller, token_number)
    state.token.transfer(ak_2Xu6d6W4UJBWyvBVJQRHASbQHQ1vjBA7d1XUeY8SwwgzssZVHK, token_number * 10 / 100)
    state.token.transfer(ak_2MHJv6JcdcfpNvu4wRDZXWzq8QSxGbhUfhMLR7vUPzRFYsDFw6, token_number * 5 / 100)
    let account = {account = account_data.account ,count = account_data.count , height = Chain.block_height}
    put(state{accounts[Call.caller] = account})
    token_number

  private stateful function benefitsAddress() =
    let account_data = getAccount(Call.caller)
    if(Chain.block_height - account_data.height > 0 && account_data.height > 0)
      let token_number = 300000000000 * (account_data.count / state.decimals) * (Chain.block_height - account_data.height)
      state.token.transfer(Call.caller, token_number)
      state.token.transfer(ak_2Xu6d6W4UJBWyvBVJQRHASbQHQ1vjBA7d1XUeY8SwwgzssZVHK, token_number * 10 / 100)
      state.token.transfer(ak_2MHJv6JcdcfpNvu4wRDZXWzq8QSxGbhUfhMLR7vUPzRFYsDFw6, token_number * 5 / 100)

  stateful entrypoint unLock(count : int) =
    if(!isAccountExist(Call.caller))
      abort("ACCOUNT_NOT_EXIST")
    let accountData = getAccount(Call.caller)
    if(accountData.count < count)
      abort("ACCOUNT_COUNT_ERROR")
    benefitsAddress()
    let account = {account = accountData.account , count = accountData.count - count , height = Chain.block_height}
    Chain.spend(accountData.account, count)
    put(state{accounts[accountData.account] = account,all_count = state.all_count - count})
    count

	`



    let nodeUrl = 'https://node.aeasy.io';
    let compilerUrl = 'https://compiler.aeasy.io';
    const contractDefiV2Lock = function (secretKey, publicKey, ctId, amount) {
        Ae.Node({url: nodeUrl}).then(node => {
            Ae.Universal({
                compilerUrl: 'https://compiler.aeasy.io',
                nodes: [{name: 'local', instance: node}],
                accounts: [Ae.MemoryAccount({
                    keypair: {
                        secretKey: secretKey,
                        publicKey: publicKey
                    }
                }),],
                address: publicKey,
            }).then(client => {
                console.log("contractEncodeCall");
                client.contractEncodeCall(sourceCodeDefiV2, 'lock', []).then(callDataCall => {
                    console.log("contractCall");
                    client.contractCall(sourceCodeDefiV2, ctId, "lock", callDataCall, {amount: Ae.AmountFormatter.toAettos(amount)}).then(callResult => {
                        console.log("decode");
                        callResult.decode().then(callResultDecode => {
                            console.log("sucess");
                            console.log(callResultDecode);

                        }).catch(err => {
                            console.log("error");
                            console.log(err.toString());
                        })
                    }).catch(err => {
                        console.log("error");
                        console.log(err);
                        var decode = err.toString().split('Decoded: ')
                        var msg = decode[1].substring(1, decode[1].length - 4)
                        console.log(msg);

                    })
                }).catch(err => {
                    console.log("error");
                    console.log(err.toString());
                })
            }).catch(err => {
                console.log("error");
                console.log(err.toString());
            })
        }).catch(err => {
            console.log("error");
            console.log(err.toString());
        });
    };

	const contractDefiV2UnLock = function (secretKey, publicKey, ctId, amount) {
		Ae.Node({url: nodeUrl}).then(node => {
			Ae.Universal({
				compilerUrl: 'https://compiler.aeasy.io',
				nodes: [{name: 'local', instance: node}],
				accounts: [Ae.MemoryAccount({
					keypair: {
						secretKey: secretKey,
						publicKey: publicKey
					}
				}),],
				address: publicKey,
			}).then(client => {
				console.log("contractEncodeCall");
				client.contractEncodeCall(sourceCodeDefiV2, 'unLock', [ Ae.AmountFormatter.toAettos(amount)]).then(callDataCall => {
					console.log("contractCall");
					client.contractCall(sourceCodeDefiV2, ctId, "unLock", callDataCall, {}).then(callResult => {
						console.log("decode");
						callResult.decode().then(callResultDecode => {
							console.log("sucess");
							console.log(callResultDecode);

						}).catch(err => {
							console.log("error");
							console.log(err.toString());
						})
					}).catch(err => {
						console.log("error");
						console.log(err);
						var decode = err.toString().split('Decoded: ')
						var msg = decode[1].substring(1, decode[1].length - 4)
						console.log(msg);

					})
				}).catch(err => {
					console.log("error");
					console.log(err.toString());
				})
			}).catch(err => {
				console.log("error");
				console.log(err.toString());
			})
		}).catch(err => {
			console.log("error");
			console.log(err.toString());
		});
	};
	const contractDefiV2Benefits = function (secretKey, publicKey, ctId) {
		Ae.Node({url: nodeUrl}).then(node => {
			Ae.Universal({
				compilerUrl: 'https://compiler.aeasy.io',
				nodes: [{name: 'local', instance: node}],
				accounts: [Ae.MemoryAccount({
					keypair: {
						secretKey: secretKey,
						publicKey: publicKey
					}
				}),],
				address: publicKey,
			}).then(client => {
				console.log("contractEncodeCall");
				client.contractEncodeCall(sourceCodeDefiV2, 'benefits', []).then(callDataCall => {
					console.log("contractCall");
					client.contractCall(sourceCodeDefiV2, ctId, "benefits", callDataCall, {}).then(callResult => {
						console.log("decode");
						callResult.decode().then(callResultDecode => {
							console.log("sucess");
							console.log(callResultDecode);

						}).catch(err => {
							console.log("error");
							console.log(err.toString());
						})
					}).catch(err => {
						console.log("error");
						console.log(err);
						var decode = err.toString().split('Decoded: ')
						var msg = decode[1].substring(1, decode[1].length - 4)
						console.log(msg);

					})
				}).catch(err => {
					console.log("error");
					console.log(err.toString());
				})
			}).catch(err => {
				console.log("error");
				console.log(err.toString());
			})
		}).catch(err => {
			console.log("error");
			console.log(err.toString());
		});
	};


	//The three methods, the operating process is to lock the AE first, then collect the proceeds according to the height, and then retrieve the pledged AE at any time
    //The order of calls is this: the contractDefiV2Lock() -> contractDefiV2Benefits() -> contractDefiV2UnLock()
    //


    // contractDefiV2Lock('d03826de64d010f683b4aee0ac67e074e01725bb6f94c6d26942ab5a5671886a5e88d722246295cefec3143d2cf2212347aac960d0b3ea4abe03fba86ce0dc2e', 'ak_idkx6m3bgRr7WiKXuB8EBYBoRqVsaSc6qo4dsd23HKgj3qiCF', 'ct_SNM68L9pEym92bBf3ZJjzzuB9eyCtVhouHB3Qq5SpyU9Ccn2F', '1')
    // contractDefiV2Benefits('d03826de64d010f683b4aee0ac67e074e01725bb6f94c6d26942ab5a5671886a5e88d722246295cefec3143d2cf2212347aac960d0b3ea4abe03fba86ce0dc2e', 'ak_idkx6m3bgRr7WiKXuB8EBYBoRqVsaSc6qo4dsd23HKgj3qiCF', 'ct_SNM68L9pEym92bBf3ZJjzzuB9eyCtVhouHB3Qq5SpyU9Ccn2F')
    // contractDefiV2UnLock('d03826de64d010f683b4aee0ac67e074e01725bb6f94c6d26942ab5a5671886a5e88d722246295cefec3143d2cf2212347aac960d0b3ea4abe03fba86ce0dc2e', 'ak_idkx6m3bgRr7WiKXuB8EBYBoRqVsaSc6qo4dsd23HKgj3qiCF', 'ct_SNM68L9pEym92bBf3ZJjzzuB9eyCtVhouHB3Qq5SpyU9Ccn2F', '1')

</script>
</body>
</html>

@hanssv.chain Hanssv, do you have time to audit the code for me :heart_eyes:

1 Like

I am out of office today, so I probably won’t be able to look at it. Sorry!

Then when you have time, Anytime you want, :grinning:you are welcome to help the audit the contract code

1 Like

Great, great work. When will this aepp onlien? I can’t wait to experience it.

1 Like

awesome,awesome.

As a participant, I hope this contract is secure, free of bugs and backdoors, and ensures the security of assets. There are very limited developers in Sophia right now, and you are the best Sophia developers. Therefore, in order to launch aepp as soon as possible, the community hopes that the official as a third party to participate in the review of the code. Thank you. @hanssv.chain @philipp.chain @milenradkov.chain @gorbak25