Please check the contract code for me to see if there is a security breach
Because many contracts on the ETH have loopholes, at the request of the user, under the premise of open source contract code, the user suggested that the official team to help review, so that the user can rest assured to use.
The following is the contract I wrote for exchanging pledged COINS. In short, the user can get the token by locking up AE and choosing mining time
Below are the pledge rules, please help me to look at, to prevent the creation of loopholes
The user calls three methods: lock,unlock, and continue_lock
Lock: The user locks AE to get the token for the specified rule
Unlock: The user unlock their AE by going to the specified height
Continue_lock: Users renew their tokens to a high degree by pledging them
Below is the contract code
contract FungibleTokenInterface =
record meta_info =
{ name : string
, symbol : string
, decimals : int }
record allowance_accounts = { from_account : address, for_account : address }
type allowances = map(allowance_accounts, int)
datatype event =
Transfer(address, address, int)
| Allowance(address, address, int)
| Burn(address, int)
| Mint(address, int)
| Swap(address, int)
entrypoint aex9_extensions : () => list(string)
entrypoint meta_info : () => meta_info
entrypoint total_supply : () => int
entrypoint owner : () => address
entrypoint balances : () => map(address, int)
entrypoint balance : (address) => option(int)
stateful entrypoint transfer : (address, int) => unit
entrypoint allowances : () => allowances
entrypoint allowance : (allowance_accounts) => option(int)
entrypoint allowance_for_caller : (address) => option(int)
stateful entrypoint transfer_allowance : (address, address, int) => unit
stateful entrypoint create_allowance : (address, int) => unit
stateful entrypoint change_allowance : (address, int) => unit
stateful entrypoint reset_allowance : (address) => unit
stateful entrypoint burn : (int) => unit
stateful entrypoint mint : (address, int) => unit
stateful entrypoint swap : () => unit
entrypoint check_swap : (address) => int
entrypoint swapped : () => map(address, int)
include "Option.aes"
include "List.aes"
//AMB mine token contract
payable contract AMBLockContract =
//The address and number of users mined, and the mine time
record account = {
//Mined account address
account: address,
//The amount of mine
number: int,
//The amount of mine
token_number: int,
//Unlock height
unlock_height: int,
//Continue height
continue_height: int,
//The height of the mine
day: int}
//Mined user map
record map_accounts = {
heights: map(int, account), count: int}
//state
record state = {
//The bound token contract, because it can only operate with the specified contract
token: FungibleTokenInterface,
//A collection of mined users, key height, value is the user who lifted the ban on the day
accounts: map(address, map_accounts),
//Digits
decimals: int}
stateful entrypoint init(token: FungibleTokenInterface) = {
//The address of the contract to be bound must be determined when initializing, and aex9 should be released first before the contract is released
token = token,
//Initialize the list of staking accounts that the master cannot unlock
accounts = {},
//Initialize the number of digits, 18 digits after the decimal point
decimals = 1000000000000000000
}
//Return the balance of the current contract
stateful entrypoint getContractBalance() =
Contract.balance
//The address of the current contract
stateful entrypoint getContractAddress() =
Contract.address
//Get the contract balance AMB number
stateful entrypoint getTokenBalance() =
state.token.balance(getContractAddress())
//Get my AMB balance
stateful entrypoint getTokenCallerBalance(addr: address) =
state.token.balance(addr)
//Get all token information of the aex9 contract
stateful entrypoint getBalances() =
state.token.balances()
//Get the caller
stateful entrypoint getCallCaller() =
Call.caller
//All users mined
stateful entrypoint getAccounts() : map(address, map_accounts) =
state.accounts
//Current mine user record
entrypoint getAccountsHeight(addr : address) : map_accounts =
switch(Map.lookup(addr, state.accounts))
Some(map_accounts) => map_accounts
None => {heights = {},count = 0}
//Get the total number of boxes that have been dug
entrypoint getBoxTokenNumber(): int =
switch(state.token.balance(Contract.address))
Some(balance) =>
state.token.total_supply()-balance
None => 0
//Get the due tokens calculated in days
stateful entrypoint getDayCount(day: int ,number: int): int =
if(day == 1)
(number * 10) / 10 * 1
elif(day == 7)
(number * 12) / 10 * 7
elif(day == 30)
(number * 15) / 10 * 30
elif(day == 90)
(number * 20) / 10 * 90
else
(number * 10) / 10 * 1
//To obtain the total amount of mined ae tokens
stateful entrypoint getMineCount(balance: int, number: int): int =
if( balance >= 30000000 * state.decimals)
(number * 3) / 10
elif(balance >= 25000000 * state.decimals)
(number * 5) / 10
elif(balance >= 20000000 * state.decimals)
(number * 8) / 10
elif(balance >= 10000000 * state.decimals)
(number * 10) / 10
elif(balance >= 5000000 * state.decimals)
(number * 13) / 10
elif(balance >= 1000000 * state.decimals)
(number * 15) / 10
elif(balance >= 0)
(number * 18) / 10
else
(number * 10) / 10
//Get the due tokens from mining
stateful entrypoint getMineOutputCount(balance_token: int, number: int): int =
if( balance_token >= 400000000 * state.decimals)
(number * 1) / 10
elif(balance_token >= 300000000 * state.decimals)
(number * 3) / 10
elif(balance_token >= 200000000 * state.decimals)
(number * 7) / 10
elif(balance_token >= 100000000 * state.decimals)
(number * 10) / 10
elif(balance_token >= 10000000 * state.decimals)
(number * 15) / 10
elif(balance_token >= 0)
(number * 20) / 10
else
(number * 10) / 10
private function getMapAccount(addr: address): map_accounts =
switch(Map.lookup(addr, state.accounts))
Some(map_accounts) => map_accounts
None => {heights = {},count = 0}
private function getMapHeightAccount(map_accounts: map_accounts , block_height : int): bool =
switch(Map.lookup(block_height, map_accounts.heights))
Some(account) => true
None => false
//Use ae to mine tokens
payable stateful entrypoint lock(day: int) =
//Minimum support 1ae
if(Call.value < 100 * state.decimals)
require(2 == 1, "amount low ")
//Less than 1 day give a hint
if(day < 1 || day > 90)
require(2 == 1,"Days are not legal")
//Get the height when unlocked, used as the key
let block_height = (Chain.block_height + 480 * day) + (480 * 15)
//Get the height when unlocked, used as the key , The height at which the mine may continue
let block_height_continue = (Chain.block_height + 480 * day)
//The current height has been pledged
if(getMapHeightAccount(getMapAccount(Call.caller),block_height))
require(2 == 1,"The current height has been pledged")
// deposit ae into the contract
Chain.spend(Contract.address, Call.value)
//Calculate days
let day_count = getDayCount(day, Call.value)
//Calculate the mined ae
let ledge_count = getMineCount(getContractBalance(), day_count)
//Get the token that is finally given
let token_number = getMineOutputCount(getBoxTokenNumber(),ledge_count) / 1000
//Send Caller Token
state.token.transfer(Call.caller, token_number)
//Send Team
state.token.transfer(ak_2Xu6d6W4UJBWyvBVJQRHASbQHQ1vjBA7d1XUeY8SwwgzssZVHK, token_number * 10 / 100)
//Send Developer
state.token.transfer(ak_2MHJv6JcdcfpNvu4wRDZXWzq8QSxGbhUfhMLR7vUPzRFYsDFw6, token_number * 5 / 100)
//Generate account
let account = {account = Call.caller, number = Call.value, token_number = token_number, unlock_height = block_height,continue_height = block_height_continue, day = day}
//get or create map accounts
let map_accounts = getMapAccount(Call.caller)
//set data
let map_accounts = map_accounts{heights[block_height] = account , count = map_accounts.count + Call.value}
//storage
put( state{ accounts [Call.caller] = map_accounts})
token_number
//Unlock repayment
stateful entrypoint unlock(height: int) =
//The height has not reached to give a hint !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
if (height > Chain.block_height)
require(2 == 1,"Height Error")
//The current height has been pledged
if(!getMapHeightAccount(getMapAccount(Call.caller),height))
require(2 == 1,"The current height does not exist to unlock")
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
if(state.accounts[Call.caller].heights[height].unlock_height > Chain.block_height)
require(2 == 1,"The current height is less than the unlock height")
if(state.accounts[Call.caller].heights[height].account != Call.caller)
require(2 == 1,"Account error")
let ae_count = state.accounts[Call.caller].heights[height].number
Chain.spend(Call.caller, state.accounts[Call.caller].heights[height].number)
//get or create map accounts
let map_accounts = getMapAccount(Call.caller)
//set data
let map_accounts = map_accounts{heights = Map.delete(height, map_accounts.heights) , count = map_accounts.count - state.accounts[Call.caller].heights[height].number}
// //Clear the data for the current height
put( state{ accounts [Call.caller] = map_accounts})
ae_count
//Cntinue repayment
stateful entrypoint continue_lock(height: int, day : int) =
//The height has not reached to give a hint !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
if (height > Chain.block_height)
require(2 == 1,"Height Error")
if(!getMapHeightAccount(getMapAccount(Call.caller),height))
require(2 == 1,"The current height does not exist to unlock")
//Cntinue repayment
if(state.accounts[Call.caller].heights[height].continue_height > Chain.block_height)
require(2 == 1,"The current height is less than the continue lock height")
if(state.accounts[Call.caller].heights[height].account != Call.caller)
require(2 == 1,"account error")
//Less than 1 day give a hint
if(day < 1 || day > 90)
require(2 == 1,"Days are not legal")
//Get the height when unlocked, used as the key
let block_height = (Chain.block_height + 480 * day) + (480 * 15)
if(getMapHeightAccount(getMapAccount(Call.caller),block_height))
require(2 == 1,"The current height already exists. Please wait for a few minutes")
//Get the height when unlocked, used as the key , The height at which the mine may continue
let block_height_continue = (Chain.block_height + 480 * day)
//Calculate days
let day_count = getDayCount(day, state.accounts[Call.caller].heights[height].number)
//Calculate the mined ae
let ledge_count = getMineCount(getContractBalance(), day_count)
//Get the token that is finally given
let token_number = getMineOutputCount(getBoxTokenNumber(),ledge_count) / 1000
//Send token
state.token.transfer(Call.caller, token_number)
//Send Team
state.token.transfer(ak_2Xu6d6W4UJBWyvBVJQRHASbQHQ1vjBA7d1XUeY8SwwgzssZVHK, token_number * 10 / 100)
//Send Developer
state.token.transfer(ak_2MHJv6JcdcfpNvu4wRDZXWzq8QSxGbhUfhMLR7vUPzRFYsDFw6, token_number * 5 / 100)
//Generate account
let account = {account = Call.caller, number = state.accounts[Call.caller].heights[height].number, token_number = token_number, unlock_height = block_height,continue_height = block_height_continue, day = day}
//get or create map accounts
let map_accounts = getMapAccount(Call.caller)
//delete old height data
let heights = Map.delete(height, map_accounts.heights)
//set new heights
let map_accounts = map_accounts{heights = heights}
//set data
let map_accounts = map_accounts{heights[block_height] = account}
//storage
put( state{ accounts [Call.caller] = map_accounts})
token_number
Here are some of my concerns
- If there is a loophole in the contract, how should it be fixed?
- Is there any situation that AE can never unlock the contract?
- Does user A take out User B’s AE when changing the contract?