mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
fix: Reject deposits directly to implementation
This commit is contained in:
parent
123415be61
commit
02b9641276
|
@ -9,4 +9,5 @@ library StaticATokenErrors {
|
||||||
string public constant INVALID_RECIPIENT = '5';
|
string public constant INVALID_RECIPIENT = '5';
|
||||||
string public constant INVALID_CLAIMER = '6';
|
string public constant INVALID_CLAIMER = '6';
|
||||||
string public constant ONLY_ONE_AMOUNT_FORMAT_ALLOWED = '7';
|
string public constant ONLY_ONE_AMOUNT_FORMAT_ALLOWED = '7';
|
||||||
|
string public constant ONLY_PROXY_MAY_CALL = '8';
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,17 @@ contract StaticATokenLM is
|
||||||
// user => unclaimedRewards (in RAYs)
|
// user => unclaimedRewards (in RAYs)
|
||||||
mapping(address => uint256) private _unclaimedRewards;
|
mapping(address => uint256) private _unclaimedRewards;
|
||||||
|
|
||||||
|
bool public isImplementation;
|
||||||
|
|
||||||
|
modifier onlyProxy() {
|
||||||
|
require(isImplementation == false, StaticATokenErrors.ONLY_PROXY_MAY_CALL);
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() public {
|
||||||
|
isImplementation = true;
|
||||||
|
}
|
||||||
|
|
||||||
///@inheritdoc VersionedInitializable
|
///@inheritdoc VersionedInitializable
|
||||||
function getRevision() internal pure virtual override returns (uint256) {
|
function getRevision() internal pure virtual override returns (uint256) {
|
||||||
return STATIC_ATOKEN_LM_REVISION;
|
return STATIC_ATOKEN_LM_REVISION;
|
||||||
|
@ -294,7 +305,7 @@ contract StaticATokenLM is
|
||||||
uint256 amount,
|
uint256 amount,
|
||||||
uint16 referralCode,
|
uint16 referralCode,
|
||||||
bool fromUnderlying
|
bool fromUnderlying
|
||||||
) internal returns (uint256) {
|
) internal onlyProxy returns (uint256) {
|
||||||
require(recipient != address(0), StaticATokenErrors.INVALID_RECIPIENT);
|
require(recipient != address(0), StaticATokenErrors.INVALID_RECIPIENT);
|
||||||
_updateRewards();
|
_updateRewards();
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@ import {
|
||||||
WETH9,
|
WETH9,
|
||||||
AToken,
|
AToken,
|
||||||
StaticATokenLM,
|
StaticATokenLM,
|
||||||
|
InitializableAdminUpgradeabilityProxyFactory,
|
||||||
|
StaticAToken,
|
||||||
} from '../../../../types';
|
} from '../../../../types';
|
||||||
import {
|
import {
|
||||||
impersonateAccountsHardhat,
|
impersonateAccountsHardhat,
|
||||||
|
@ -27,6 +29,7 @@ import { AbiCoder, formatEther, verifyTypedData } from 'ethers/lib/utils';
|
||||||
import { _TypedDataEncoder } from 'ethers/lib/utils';
|
import { _TypedDataEncoder } from 'ethers/lib/utils';
|
||||||
|
|
||||||
import { expect, use } from 'chai';
|
import { expect, use } from 'chai';
|
||||||
|
import { zeroAddress } from 'hardhat/node_modules/ethereumjs-util';
|
||||||
|
|
||||||
//use(solidity);
|
//use(solidity);
|
||||||
|
|
||||||
|
@ -80,6 +83,7 @@ describe('StaticATokenLM: aToken wrapper with static balances and NO liquidity m
|
||||||
let enj: ERC20;
|
let enj: ERC20;
|
||||||
let aenj: AToken;
|
let aenj: AToken;
|
||||||
|
|
||||||
|
let staticATokenImplementation: StaticATokenLM;
|
||||||
let staticAToken: StaticATokenLM;
|
let staticAToken: StaticATokenLM;
|
||||||
|
|
||||||
let snap: string;
|
let snap: string;
|
||||||
|
@ -99,8 +103,25 @@ describe('StaticATokenLM: aToken wrapper with static balances and NO liquidity m
|
||||||
aenj = ATokenFactory.connect(AENJ, userSigner);
|
aenj = ATokenFactory.connect(AENJ, userSigner);
|
||||||
stkAave = ERC20Factory.connect(STKAAVE, userSigner);
|
stkAave = ERC20Factory.connect(STKAAVE, userSigner);
|
||||||
|
|
||||||
staticAToken = await new StaticATokenLMFactory(userSigner).deploy();
|
staticATokenImplementation = await new StaticATokenLMFactory(userSigner).deploy();
|
||||||
await staticAToken.initialize(LENDING_POOL, AENJ, 'Wrapped aENJ', 'waaenj');
|
await staticATokenImplementation.initialize(LENDING_POOL, AENJ, 'Wrapped aENJ', 'waaenj');
|
||||||
|
|
||||||
|
const proxy = await new InitializableAdminUpgradeabilityProxyFactory(userSigner).deploy();
|
||||||
|
const encodedInitializedParams = staticATokenImplementation.interface.encodeFunctionData(
|
||||||
|
'initialize',
|
||||||
|
[LENDING_POOL, AENJ, 'Wrapped aENJ', 'waaenj']
|
||||||
|
);
|
||||||
|
|
||||||
|
await proxy['initialize(address,address,bytes)'](
|
||||||
|
staticATokenImplementation.address,
|
||||||
|
zeroAddress(),
|
||||||
|
encodedInitializedParams
|
||||||
|
);
|
||||||
|
|
||||||
|
staticAToken = StaticATokenLMFactory.connect(proxy.address, userSigner);
|
||||||
|
|
||||||
|
expect(await staticATokenImplementation.isImplementation()).to.be.eq(true);
|
||||||
|
expect(await staticAToken.isImplementation()).to.be.eq(false);
|
||||||
|
|
||||||
expect(await staticAToken.decimals()).to.be.eq(18);
|
expect(await staticAToken.decimals()).to.be.eq(18);
|
||||||
expect(await staticAToken.name()).to.be.eq('Wrapped aENJ');
|
expect(await staticAToken.name()).to.be.eq('Wrapped aENJ');
|
||||||
|
@ -264,7 +285,7 @@ describe('StaticATokenLM: aToken wrapper with static balances and NO liquidity m
|
||||||
const bGas = BigNumber.from(bReceipt['gasUsed']);
|
const bGas = BigNumber.from(bReceipt['gasUsed']);
|
||||||
|
|
||||||
expect(aGas).to.be.gt(250000);
|
expect(aGas).to.be.gt(250000);
|
||||||
expect(bGas).to.be.lt(200000);
|
expect(bGas).to.be.lt(210000);
|
||||||
|
|
||||||
await DRE.network.provider.send('evm_setAutomine', [true]);
|
await DRE.network.provider.send('evm_setAutomine', [true]);
|
||||||
await DRE.network.provider.send('evm_mine', []);
|
await DRE.network.provider.send('evm_mine', []);
|
||||||
|
@ -296,8 +317,8 @@ describe('StaticATokenLM: aToken wrapper with static balances and NO liquidity m
|
||||||
const aGas = BigNumber.from(aReceipt['gasUsed']);
|
const aGas = BigNumber.from(aReceipt['gasUsed']);
|
||||||
const bGas = BigNumber.from(bReceipt['gasUsed']);
|
const bGas = BigNumber.from(bReceipt['gasUsed']);
|
||||||
|
|
||||||
expect(aGas).to.be.lt(25000);
|
expect(aGas).to.be.lt(35000);
|
||||||
expect(bGas).to.be.lt(25000);
|
expect(bGas).to.be.lt(35000);
|
||||||
|
|
||||||
await DRE.network.provider.send('evm_setAutomine', [true]);
|
await DRE.network.provider.send('evm_setAutomine', [true]);
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
AToken,
|
AToken,
|
||||||
StaticAToken,
|
StaticAToken,
|
||||||
StaticATokenLM,
|
StaticATokenLM,
|
||||||
|
InitializableAdminUpgradeabilityProxyFactory,
|
||||||
} from '../../../../types';
|
} from '../../../../types';
|
||||||
import { IAaveIncentivesControllerFactory } from '../../../../types/IAaveIncentivesControllerFactory';
|
import { IAaveIncentivesControllerFactory } from '../../../../types/IAaveIncentivesControllerFactory';
|
||||||
import {
|
import {
|
||||||
|
@ -68,6 +69,7 @@ const LM_ERRORS = {
|
||||||
INVALID_RECIPIENT: '5',
|
INVALID_RECIPIENT: '5',
|
||||||
INVALID_CLAIMER: '6',
|
INVALID_CLAIMER: '6',
|
||||||
ONLY_ONE_AMOUNT_FORMAT_ALLOWED: '7',
|
ONLY_ONE_AMOUNT_FORMAT_ALLOWED: '7',
|
||||||
|
ONLY_PROXY_MAY_CALL: '8',
|
||||||
};
|
};
|
||||||
|
|
||||||
type tBalancesInvolved = {
|
type tBalancesInvolved = {
|
||||||
|
@ -147,6 +149,7 @@ describe('StaticATokenLM: aToken wrapper with static balances and NO liquidity m
|
||||||
|
|
||||||
let enjWhale: providers.JsonRpcSigner;
|
let enjWhale: providers.JsonRpcSigner;
|
||||||
|
|
||||||
|
let staticATokenImplementation: StaticATokenLM;
|
||||||
let staticAToken: StaticATokenLM;
|
let staticAToken: StaticATokenLM;
|
||||||
|
|
||||||
let snap: string;
|
let snap: string;
|
||||||
|
@ -167,8 +170,25 @@ describe('StaticATokenLM: aToken wrapper with static balances and NO liquidity m
|
||||||
aenj = ATokenFactory.connect(AENJ, userSigner);
|
aenj = ATokenFactory.connect(AENJ, userSigner);
|
||||||
stkAave = ERC20Factory.connect(STKAAVE, userSigner);
|
stkAave = ERC20Factory.connect(STKAAVE, userSigner);
|
||||||
|
|
||||||
staticAToken = await new StaticATokenLMFactory(userSigner).deploy();
|
staticATokenImplementation = await new StaticATokenLMFactory(userSigner).deploy();
|
||||||
await staticAToken.initialize(LENDING_POOL, AENJ, 'Wrapped aENJ', 'waaenj');
|
await staticATokenImplementation.initialize(LENDING_POOL, AENJ, 'Wrapped aENJ', 'waaenj');
|
||||||
|
|
||||||
|
const proxy = await new InitializableAdminUpgradeabilityProxyFactory(userSigner).deploy();
|
||||||
|
const encodedInitializedParams = staticATokenImplementation.interface.encodeFunctionData(
|
||||||
|
'initialize',
|
||||||
|
[LENDING_POOL, AENJ, 'Wrapped aENJ', 'waaenj']
|
||||||
|
);
|
||||||
|
|
||||||
|
await proxy['initialize(address,address,bytes)'](
|
||||||
|
staticATokenImplementation.address,
|
||||||
|
zeroAddress(),
|
||||||
|
encodedInitializedParams
|
||||||
|
);
|
||||||
|
|
||||||
|
staticAToken = StaticATokenLMFactory.connect(proxy.address, userSigner);
|
||||||
|
|
||||||
|
expect(await staticATokenImplementation.isImplementation()).to.be.eq(true);
|
||||||
|
expect(await staticAToken.isImplementation()).to.be.eq(false);
|
||||||
|
|
||||||
expect(await staticAToken.getIncentivesController()).to.be.eq(zeroAddress());
|
expect(await staticAToken.getIncentivesController()).to.be.eq(zeroAddress());
|
||||||
expect(await staticAToken.ASSET()).to.be.eq(await staticAToken.UNDERLYING_ASSET_ADDRESS());
|
expect(await staticAToken.ASSET()).to.be.eq(await staticAToken.UNDERLYING_ASSET_ADDRESS());
|
||||||
|
@ -199,6 +219,27 @@ describe('StaticATokenLM: aToken wrapper with static balances and NO liquidity m
|
||||||
await evmRevert(snap);
|
await evmRevert(snap);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Deposit ENJ directly to implementation (expect revert)', async () => {
|
||||||
|
const amountToDeposit = utils.parseEther('5');
|
||||||
|
const amountToWithdraw = MAX_UINT_AMOUNT;
|
||||||
|
|
||||||
|
// Just preparation
|
||||||
|
await waitForTx(
|
||||||
|
await enj.approve(staticATokenImplementation.address, amountToDeposit, defaultTxParams)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Depositing
|
||||||
|
await expect(
|
||||||
|
staticATokenImplementation.deposit(
|
||||||
|
userSigner._address,
|
||||||
|
amountToDeposit,
|
||||||
|
0,
|
||||||
|
true,
|
||||||
|
defaultTxParams
|
||||||
|
)
|
||||||
|
).to.be.revertedWith(LM_ERRORS.ONLY_PROXY_MAY_CALL);
|
||||||
|
});
|
||||||
|
|
||||||
it('Deposit ENJ on waaenj, then withdraw of the whole balance in underlying', async () => {
|
it('Deposit ENJ on waaenj, then withdraw of the whole balance in underlying', async () => {
|
||||||
const amountToDeposit = utils.parseEther('5');
|
const amountToDeposit = utils.parseEther('5');
|
||||||
const amountToWithdraw = MAX_UINT_AMOUNT;
|
const amountToWithdraw = MAX_UINT_AMOUNT;
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
WETH9,
|
WETH9,
|
||||||
AToken,
|
AToken,
|
||||||
StaticATokenLM,
|
StaticATokenLM,
|
||||||
|
InitializableAdminUpgradeabilityProxyFactory,
|
||||||
} from '../../../../types';
|
} from '../../../../types';
|
||||||
import {
|
import {
|
||||||
impersonateAccountsHardhat,
|
impersonateAccountsHardhat,
|
||||||
|
@ -27,6 +28,7 @@ import { AbiCoder, formatEther, verifyTypedData } from 'ethers/lib/utils';
|
||||||
import { _TypedDataEncoder } from 'ethers/lib/utils';
|
import { _TypedDataEncoder } from 'ethers/lib/utils';
|
||||||
|
|
||||||
import { expect, use } from 'chai';
|
import { expect, use } from 'chai';
|
||||||
|
import { zeroAddress } from 'ethereumjs-util';
|
||||||
|
|
||||||
//use(solidity);
|
//use(solidity);
|
||||||
|
|
||||||
|
@ -81,6 +83,7 @@ describe('StaticATokenLM: aToken wrapper with static balances and liquidity mini
|
||||||
let aweth: AToken;
|
let aweth: AToken;
|
||||||
let stkAave: ERC20;
|
let stkAave: ERC20;
|
||||||
|
|
||||||
|
let staticATokenImplementation: StaticATokenLM;
|
||||||
let staticAToken: StaticATokenLM;
|
let staticAToken: StaticATokenLM;
|
||||||
|
|
||||||
let snap: string;
|
let snap: string;
|
||||||
|
@ -97,14 +100,31 @@ describe('StaticATokenLM: aToken wrapper with static balances and liquidity mini
|
||||||
aweth = ATokenFactory.connect(AWETH, userSigner);
|
aweth = ATokenFactory.connect(AWETH, userSigner);
|
||||||
stkAave = ERC20Factory.connect(STKAAVE, userSigner);
|
stkAave = ERC20Factory.connect(STKAAVE, userSigner);
|
||||||
|
|
||||||
staticAToken = await new StaticATokenLMFactory(userSigner).deploy();
|
staticATokenImplementation = await new StaticATokenLMFactory(userSigner).deploy();
|
||||||
await staticAToken.initialize(
|
await staticATokenImplementation.initialize(
|
||||||
LENDING_POOL,
|
LENDING_POOL,
|
||||||
AWETH,
|
AWETH,
|
||||||
'Static Aave Interest Bearing WETH',
|
'Static Aave Interest Bearing WETH',
|
||||||
'stataWETH'
|
'stataWETH'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const proxy = await new InitializableAdminUpgradeabilityProxyFactory(userSigner).deploy();
|
||||||
|
const encodedInitializedParams = staticATokenImplementation.interface.encodeFunctionData(
|
||||||
|
'initialize',
|
||||||
|
[LENDING_POOL, AWETH, 'Static Aave Interest Bearing WETH', 'stataWETH']
|
||||||
|
);
|
||||||
|
|
||||||
|
await proxy['initialize(address,address,bytes)'](
|
||||||
|
staticATokenImplementation.address,
|
||||||
|
zeroAddress(),
|
||||||
|
encodedInitializedParams
|
||||||
|
);
|
||||||
|
|
||||||
|
staticAToken = StaticATokenLMFactory.connect(proxy.address, userSigner);
|
||||||
|
|
||||||
|
expect(await staticATokenImplementation.isImplementation()).to.be.eq(true);
|
||||||
|
expect(await staticAToken.isImplementation()).to.be.eq(false);
|
||||||
|
|
||||||
expect(await staticAToken.decimals()).to.be.eq(18);
|
expect(await staticAToken.decimals()).to.be.eq(18);
|
||||||
expect(await staticAToken.name()).to.be.eq('Static Aave Interest Bearing WETH');
|
expect(await staticAToken.name()).to.be.eq('Static Aave Interest Bearing WETH');
|
||||||
expect(await staticAToken.symbol()).to.be.eq('stataWETH');
|
expect(await staticAToken.symbol()).to.be.eq('stataWETH');
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
AToken,
|
AToken,
|
||||||
StaticAToken,
|
StaticAToken,
|
||||||
StaticATokenLM,
|
StaticATokenLM,
|
||||||
|
InitializableAdminUpgradeabilityProxyFactory,
|
||||||
} from '../../../../types';
|
} from '../../../../types';
|
||||||
import { IAaveIncentivesControllerFactory } from '../../../../types/IAaveIncentivesControllerFactory';
|
import { IAaveIncentivesControllerFactory } from '../../../../types/IAaveIncentivesControllerFactory';
|
||||||
import {
|
import {
|
||||||
|
@ -39,6 +40,7 @@ import {
|
||||||
} from '../../../../helpers/contracts-helpers';
|
} from '../../../../helpers/contracts-helpers';
|
||||||
import { IAaveIncentivesController } from '../../../../types/IAaveIncentivesController';
|
import { IAaveIncentivesController } from '../../../../types/IAaveIncentivesController';
|
||||||
import { deploySelfdestructTransferMock } from '../../../../helpers/contracts-deployments';
|
import { deploySelfdestructTransferMock } from '../../../../helpers/contracts-deployments';
|
||||||
|
import { zeroAddress } from 'ethereumjs-util';
|
||||||
|
|
||||||
const { expect, use } = require('chai');
|
const { expect, use } = require('chai');
|
||||||
|
|
||||||
|
@ -66,6 +68,7 @@ const LM_ERRORS = {
|
||||||
INVALID_RECIPIENT: '5',
|
INVALID_RECIPIENT: '5',
|
||||||
INVALID_CLAIMER: '6',
|
INVALID_CLAIMER: '6',
|
||||||
ONLY_ONE_AMOUNT_FORMAT_ALLOWED: '7',
|
ONLY_ONE_AMOUNT_FORMAT_ALLOWED: '7',
|
||||||
|
ONLY_PROXY_MAY_CALL: '8',
|
||||||
};
|
};
|
||||||
|
|
||||||
type tBalancesInvolved = {
|
type tBalancesInvolved = {
|
||||||
|
@ -143,6 +146,7 @@ describe('StaticATokenLM: aToken wrapper with static balances and liquidity mini
|
||||||
let aweth: AToken;
|
let aweth: AToken;
|
||||||
let stkAave: ERC20;
|
let stkAave: ERC20;
|
||||||
|
|
||||||
|
let staticATokenImplementation: StaticATokenLM;
|
||||||
let staticAToken: StaticATokenLM;
|
let staticAToken: StaticATokenLM;
|
||||||
|
|
||||||
let snap: string;
|
let snap: string;
|
||||||
|
@ -162,15 +166,31 @@ describe('StaticATokenLM: aToken wrapper with static balances and liquidity mini
|
||||||
aweth = ATokenFactory.connect(AWETH, userSigner);
|
aweth = ATokenFactory.connect(AWETH, userSigner);
|
||||||
stkAave = ERC20Factory.connect(STKAAVE, userSigner);
|
stkAave = ERC20Factory.connect(STKAAVE, userSigner);
|
||||||
|
|
||||||
staticAToken = await new StaticATokenLMFactory(userSigner).deploy();
|
staticATokenImplementation = await new StaticATokenLMFactory(userSigner).deploy();
|
||||||
await staticAToken.initialize(
|
await staticATokenImplementation.initialize(
|
||||||
LENDING_POOL,
|
LENDING_POOL,
|
||||||
AWETH,
|
AWETH,
|
||||||
'Static Aave Interest Bearing WETH',
|
'Static Aave Interest Bearing WETH',
|
||||||
'stataAAVE'
|
'stataWETH'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const proxy = await new InitializableAdminUpgradeabilityProxyFactory(userSigner).deploy();
|
||||||
|
const encodedInitializedParams = staticATokenImplementation.interface.encodeFunctionData(
|
||||||
|
'initialize',
|
||||||
|
[LENDING_POOL, AWETH, 'Static Aave Interest Bearing WETH', 'stataWETH']
|
||||||
|
);
|
||||||
|
|
||||||
|
await proxy['initialize(address,address,bytes)'](
|
||||||
|
staticATokenImplementation.address,
|
||||||
|
zeroAddress(),
|
||||||
|
encodedInitializedParams
|
||||||
|
);
|
||||||
|
|
||||||
|
staticAToken = StaticATokenLMFactory.connect(proxy.address, userSigner);
|
||||||
|
|
||||||
expect(await staticAToken.getIncentivesController()).to.be.eq(INCENTIVES_CONTROLLER);
|
expect(await staticAToken.getIncentivesController()).to.be.eq(INCENTIVES_CONTROLLER);
|
||||||
|
expect(await staticATokenImplementation.isImplementation()).to.be.eq(true);
|
||||||
|
expect(await staticAToken.isImplementation()).to.be.eq(false);
|
||||||
|
|
||||||
ctxtParams = {
|
ctxtParams = {
|
||||||
staticAToken: <StaticATokenLM>staticAToken,
|
staticAToken: <StaticATokenLM>staticAToken,
|
||||||
|
@ -194,6 +214,28 @@ describe('StaticATokenLM: aToken wrapper with static balances and liquidity mini
|
||||||
await evmRevert(snap);
|
await evmRevert(snap);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Deposit WETH directly to implementation (expect revert)', async () => {
|
||||||
|
const amountToDeposit = utils.parseEther('5');
|
||||||
|
const amountToWithdraw = MAX_UINT_AMOUNT;
|
||||||
|
|
||||||
|
// Just preparation
|
||||||
|
await waitForTx(await weth.deposit({ value: amountToDeposit }));
|
||||||
|
await waitForTx(
|
||||||
|
await weth.approve(staticATokenImplementation.address, amountToDeposit, defaultTxParams)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Depositing
|
||||||
|
await expect(
|
||||||
|
staticATokenImplementation.deposit(
|
||||||
|
userSigner._address,
|
||||||
|
amountToDeposit,
|
||||||
|
0,
|
||||||
|
true,
|
||||||
|
defaultTxParams
|
||||||
|
)
|
||||||
|
).to.be.revertedWith(LM_ERRORS.ONLY_PROXY_MAY_CALL);
|
||||||
|
});
|
||||||
|
|
||||||
it('Deposit WETH on stataWETH, then withdraw of the whole balance in underlying', async () => {
|
it('Deposit WETH on stataWETH, then withdraw of the whole balance in underlying', async () => {
|
||||||
const amountToDeposit = utils.parseEther('5');
|
const amountToDeposit = utils.parseEther('5');
|
||||||
const amountToWithdraw = MAX_UINT_AMOUNT;
|
const amountToWithdraw = MAX_UINT_AMOUNT;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user