Skip to content

Commit

Permalink
Merge pull request #38 from provable-things/maybe-set-allowance-on-pe…
Browse files Browse the repository at this point in the history
…g-in

feat(CLI): <- check allowance when peggin in via that and attempt to set if insufficient
  • Loading branch information
gskapka authored Jul 6, 2022
2 parents 874351f + 213d624 commit d333200
Show file tree
Hide file tree
Showing 10 changed files with 164 additions and 68 deletions.
29 changes: 0 additions & 29 deletions lib/check-allowance.js

This file was deleted.

24 changes: 24 additions & 0 deletions lib/check-token-balance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const { BigNumber } = require('ethers')
const { logic } = require('ptokens-utils')
const { getTokenContract } = require('./get-token-contract')
const { CONTRACT_CALL_TIME_OUT_TIME } = require('./constants')

const checkTokenBalanceIsSufficient = (_amount, _tokenOwnerAddress, _tokenAddress, _signer) =>
console.info(`✔ Checking spender ${_tokenOwnerAddress} has sufficient balance of token ${_tokenAddress}...`) ||
logic.racePromise(
CONTRACT_CALL_TIME_OUT_TIME,
getTokenContract(_tokenAddress, _signer).balanceOf,
[ _tokenOwnerAddress ],
)
.then(_tokenBalance => {
if (_tokenBalance.lt(BigNumber.from(_amount))) {
return Promise.reject(
new Error(`Insufficient token balance to peg in! Got ${_tokenBalance}, need ${_amount}!`)
)
} else {
console.info(`✔ Token balance of ${_tokenBalance} is sufficient!`)
return
}
})

module.exports = { checkTokenBalanceIsSufficient }
34 changes: 34 additions & 0 deletions lib/check-token-is-supported.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const { logic } = require('ptokens-utils')
const { getEthersContract } = require('./utils')
const { CONTRACT_CALL_TIME_OUT_TIME } = require('./constants')

const VAULT_ABI_FRAGMENT = [{
'type': 'function',
'stateMutability': 'view',
'name': 'isTokenSupported',
'outputs': [{ 'internalType': 'bool', 'name': '', 'type': 'bool' }],
'inputs': [{ 'internalType': 'address', 'name': '_token', 'type': 'address' }],
}]

const getVaultContract = (_address, _provider) =>
getEthersContract(_address, VAULT_ABI_FRAGMENT, _provider)

const checkTokenIsSupportedInVault = (_vaultAddress, _tokenAddress, _provider) =>
console.info(`✔ Checking token @ ${_tokenAddress} is supported in vault @ ${_vaultAddress}...`) ||
logic.racePromise(
CONTRACT_CALL_TIME_OUT_TIME,
getVaultContract(_vaultAddress, _provider).isTokenSupported,
[ _tokenAddress ],
)
.then(_tokenIsSupported => {
if (_tokenIsSupported) {
console.info('✔ Token is supported in vault contract!')
return
} else {
return Promise.reject(
new Error(`Token @ ${_tokenAddress} is NOT supported in vault contract @ ${_vaultAddress}!`)
)
}
})

module.exports = { checkTokenIsSupportedInVault }
1 change: 1 addition & 0 deletions lib/constants.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module.exports = {
ENDPOINT_ENV_VAR_KEY: 'ENDPOINT',
CONTRACT_CALL_TIME_OUT_TIME: 3000,
ETHERSCAN_ENV_VAR_KEY: 'ETHERSCAN_API_KEY',
HARDHAT_CONFIG_FILE_NAME: 'hardhat.config.js',
FLATTENED_CONTRACT_FILE_NAME: 'flattened.sol',
Expand Down
49 changes: 33 additions & 16 deletions lib/get-token-contract.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,37 @@
/* eslint-disable-next-line no-shadow */
const ethers = require('ethers')
const { getEthersContract } = require('./utils')

const TOKEN_ABI = [{
'inputs': [
{ 'internalType': 'address', 'name': 'holder', 'type': 'address' },
{ 'internalType': 'address', 'name': 'spender', 'type': 'address' }
],
'name': 'allowance',
'outputs': [ { 'internalType': 'uint256', 'name': '', 'type': 'uint256' } ],
'stateMutability': 'view',
'type': 'function'
}]
const TOKEN_ABI = [
{
'inputs': [
{ 'internalType': 'address', 'name': 'holder', 'type': 'address' },
{ 'internalType': 'address', 'name': 'spender', 'type': 'address' }
],
'name': 'allowance',
'outputs': [ { 'internalType': 'uint256', 'name': '', 'type': 'uint256' } ],
'stateMutability': 'view',
'type': 'function'
},
{
'inputs': [ { 'internalType': 'address', 'name': 'tokenHolder', 'type': 'address' } ],
'name': 'balanceOf',
'outputs': [ { 'internalType': 'uint256', 'name': '', 'type': 'uint256' } ],
'stateMutability': 'view',
'type': 'function'
},
{
'inputs': [
{ 'internalType': 'address', 'name': 'spender', 'type': 'address' },
{ 'internalType': 'uint256', 'name': 'value', 'type': 'uint256' }
],
'name': 'approve',
'outputs': [ { 'internalType': 'bool', 'name': '', 'type': 'bool' } ],
'stateMutability': 'nonpayable',
'type': 'function'
},
]

const getTokenContract = (_address, _signer) => {
console.info(`✔ Getting token contract @ '${_address}'...`)
return new ethers.Contract(_address, TOKEN_ABI, _signer)
}
const getTokenContract = (_address, _signer) =>
console.info(`✔ Getting token contract @ '${_address}'...`) ||
getEthersContract(_address, TOKEN_ABI, _signer)

module.exports = { getTokenContract }
19 changes: 9 additions & 10 deletions lib/get-vault-contract.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
/* eslint-disable-next-line no-shadow */
const ethers = require('ethers')
const { curry } = require('ramda')
const { getEthersContract } = require('./utils')
const { getProvider } = require('./get-provider')
const { checkEndpoint } = require('./check-endpoint')
const { getVaultAbi } = require('./get-contract-artifacts')
const { ENDPOINT_ENV_VAR_KEY } = require('./constants')
const { getEthersWallet } = require('./get-ethers-wallet')
const { getVaultAbi } = require('./get-contract-artifacts')
const { getEnvConfiguration } = require('./get-env-configuration')
const { getEnvironmentVariable } = require('./get-environment-variable')

const getEthersContract = curry((_address, _abi, _signer) => {
console.info(`✔ Getting contract @ '${_address}'...`)
return Promise.resolve(new ethers.Contract(_address, _abi, _signer))
})
const getVaultEthersContract = curry((_address, _signer) =>
console.info('✔ Getting vault contract...') ||
getVaultAbi().then(_abi => getEthersContract(_address, _abi, _signer))
)

const getVaultContract = _deployedContractAddress =>
console.info(`✔ Getting pToken contract @ '${_deployedContractAddress}'...`) ||
getEnvConfiguration()
.then(() => getEnvironmentVariable(ENDPOINT_ENV_VAR_KEY))
.then(getProvider)
.then(checkEndpoint)
.then(_endpoint => Promise.all([ getEthersWallet(_endpoint), getVaultAbi() ]))
.then(([ _wallet, _abi ]) => getEthersContract(_deployedContractAddress, _abi, _wallet))
.then(_contract => console.info('✔ Contract retrieved!') || _contract)
.then(getEthersWallet)
.then(getVaultEthersContract(_deployedContractAddress))
.then(_contract => console.info('✔ Vault contract retrieved!') || _contract)

module.exports = { getVaultContract }
33 changes: 33 additions & 0 deletions lib/maybe-approve-allowance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const { BigNumber } = require('ethers')
const { logic } = require('ptokens-utils')
const { getTokenContract } = require('./get-token-contract')
const { CONTRACT_CALL_TIME_OUT_TIME } = require('./constants')

const approveAllowance = (_amount, _vaultAddress, _tokenAddress, _signer) =>
console.info(`✔ Approving vault to spend ${_amount} tokens, please wait for mining...`) ||
getTokenContract(_tokenAddress, _signer)
.approve(_vaultAddress, _amount)
.then(_tx => _tx.wait())
.then(_ => console.info('✔ Approval succeeded! Continuing with peg in...'))

const maybeApproveAllowance = (_amount, _vaultAddress, _ownerAddress, _tokenAddress, _signer) =>
console.info(`✔ Checking spender ${_vaultAddress} has sufficient allowance for token ${_tokenAddress}...`) ||
logic.racePromise(
CONTRACT_CALL_TIME_OUT_TIME,
getTokenContract(_tokenAddress, _signer).allowance,
[ _ownerAddress, _vaultAddress ],
)
.then(_allowance => {
if (_allowance.lt(BigNumber.from(_amount))) {
// NOTE: At least one of the tokens we bridge has a special approval mechanism whereby if we
// want to change an allowance that is already > 0, we must first set it to 0 and then to our
// desired amount. This logic does NOT currently handle this case.
console.info('✘ Allowance is not sufficient!')
return approveAllowance(_amount, _vaultAddress, _tokenAddress, _signer)
} else {
console.info(`✔ Allowance of ${_allowance} is already sufficient!`)
return
}
})

module.exports = { maybeApproveAllowance }
34 changes: 22 additions & 12 deletions lib/peg-in.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,38 @@
const { getVaultContract } = require('./get-vault-contract')
const { checkAllowanceIsSufficient } = require('./check-allowance')
const { maybeApproveAllowance } = require('./maybe-approve-allowance')
const { callFxnInContractAndAwaitReceipt } = require('./contract-utils')
const { checkTokenBalanceIsSufficient } = require('./check-token-balance')
const { checkTokenIsSupportedInVault } = require('./check-token-is-supported')

const pegIn = (
_deployedContractAddress,
_vaultAddress,
_amount,
_tokenAddress,
_destinationAddress,
_destinactionChainId,
_userData = '0x',
) =>
console.info('✔ Pegging in...') ||
getVaultContract(_deployedContractAddress)
.then(_vaultContact => Promise.all([ _vaultContact, _vaultContact.signer.getAddress() ]))
.then(([ _vaultContract, _ownerAddress ]) =>
getVaultContract(_vaultAddress)
.then(_vaultContract => Promise.all([ _vaultContract, _vaultContract.signer.getAddress() ]))
.then(([ _vaultContract, _tokenOwnerAddress ]) =>
Promise.all([
_vaultContract,
checkAllowanceIsSufficient(
_amount,
_deployedContractAddress, // NOTE: This is the "spender" address, the vault in this case!
_ownerAddress,
_tokenAddress,
_vaultContract.provider,
)
_tokenOwnerAddress,
checkTokenIsSupportedInVault(_vaultAddress, _tokenAddress, _vaultContract.provider),
])
)
.then(([ _vaultContract, _tokenOwnerAddress, ]) =>
Promise.all([
_vaultContract,
_tokenOwnerAddress,
checkTokenBalanceIsSufficient(_amount, _tokenOwnerAddress, _tokenAddress, _vaultContract.provider)
])
)
.then(([ _vaultContract, _tokenOwnerAddress ]) =>
Promise.all([
_vaultContract,
maybeApproveAllowance(_amount, _vaultAddress, _tokenOwnerAddress, _tokenAddress, _vaultContract.signer)
])
)
.then(([ _vaultContract ]) =>
Expand Down
7 changes: 7 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const {
has,
prop,
curry,
} = require('ramda')
/* eslint-disable-next-line no-shadow */
const ethers = require('ethers')
Expand Down Expand Up @@ -46,11 +47,17 @@ const maybeHandleEtherscanTrimErrMsg = _err =>
))
: Promise.reject(_err)

const getEthersContract = curry((_address, _abi, _signer) => {
console.info(`✔ Getting contract @ '${_address}'...`)
return new ethers.Contract(_address, _abi, _signer)
})

module.exports = {
maybeHandleEtherscanTrimErrMsg,
maybeStripHexPrefix,
maybeAddHexPrefix,
shortenEthAddress,
getEthersContract,
checkEthAddress,
getKeyFromObj,
checkIsHex,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ptokens-erc20-vault-smart-contract",
"version": "2.5.0",
"version": "2.6.0",
"description": "The pToken ERC20 vault smart-contract & CLI",
"main": "cli.js",
"scripts": {
Expand Down

0 comments on commit d333200

Please sign in to comment.