Context
The context can be found in this Notion page.
Pallet configuration
Name
Config Items
PalletId - a PalletId used to derive the on-chain accounts for the automation and the buffer pool
Storage Items
NablaRouterAddress - the address of Nabla’s router smart contract
EurcErc20Address - the address of the ERC-20 wrapper contract for the EURC token
BrzErc20Address - the address of the ERC-20 wrapper contract for the BRZ token
OfframpAccount - the address of the designated off-ramping account. !TBD! This might not be required based on the EUR issuer
ReferenceToEurAmount - a map from Reference -> Balance which is used to store the desired target amount of EUR that were initially shown to the user upon creating a transfer request in PendulumPay. We store the amount and not an exchange rate because this way we don't have to incorporate any (on-ramp or XCM) fees that might have been deducted from the BRZ amount until it arrives on our parachain.
AuthorizedAccounts - a list of authorized accounts that are allowed to add entries to the ReferenceToEurAmount map
Extrinsics
setNablaRouterAddress(address) - sets the address of the router contract. Root extrinsic
setErc20Adresses(eurcAddress, brzAddress) - sets the addresses of the erc-20 contracts. Root extrinsic
setOfframpAccount(address) - sets the address of the offramping account. Root extrinsic
addEurAmountForReference(reference, amount) - adds an entry to the ReferenceToEurAmount. Only accounts registered in AuthorizedAccounts are allowed to call this extrinsic.
addAuthorizedAccount(account) - adds an authorized account to AuthorizedAccounts. Callable from Root and other authorized accounts
removeAuthorizedAccount(account) - removes an authorized account from AuthorizedAccounts. Callable from Root and other authorized accounts.
chargeBufferPoolAccount(currency) - transfers tokens to the pallet's buffer pool account. This is a mere helper function that makes it easy to charge the pool without having to know the address of the buffer pool. Callable from anyone. We don't need to limit the currencies that can be used with this function, but generally, it would only be used to charge the buffer pool with BRZ or EURC tokens.
Functions (not exposed as extrinsic but available to other pallets)
processEurcOfframp(amount, reference) - this function is called by the XCM config of our runtime. It handles the conversion from BRZ → EUR and transfers the tokens to the offramping account. amount is the amount of BRZ tokens that are to be offramped and reference is the reference that was encoded by the MultiLocation
getPalletAccount() - this function returns the account of this pallet, derived from the PalletId configuration parameter. This can be done with <T as Config>::PalletId::get().into_sub_account_truncating(0)
getBufferPoolAccount() - this function returns the account of the pool buffer, derived from the PalletId configuration parameter. This can be done with <T as Config>::PalletId::get().into_sub_account_truncating(1)
Events
TransferProcessed(reference, amount, currency) - emitted after sending the swapped tokens to the designated offramping account.
TransferPaused(reference, amount, currency) - emitted when the offramp cannot be executed with the targeted amount. This happens if the buffer pool does not have enough funds to cover the rest or if a Nabla swap is too expensive.
Coupling to other pallets
- Contracts - needed to call the nabla contracts
- Tokens - needed for the transfer
- System - needed to set the remark
Processing the offramp
Prerequisites
Upon receiving an XCM transfer of BRZ from Moonbeam targeting our automation pallet, the XCM config deposits the respective amount of BRZ into this pallet’s on-chain account (returned by getPalletAccount()) and calls the processEurcOfframp() function, supplying the processed amount of BRZ and the reference number (which is encoded in the MultiLocation).
Implementation of the processEurcOfframp(amount, reference) function
The processEurcOfframp() function checks if there is an entry for the supplied reference parameter in the ReferenceToEurAmount map. If so, it uses the call() function of the Contracts pallet to call the swapExactTokensForTokens() function on Nabla’s router smart contract to swap the amount of BRZ tokens to EURC tokens. As a result, it will receive the corresponding amount of EURC on its on-chain account.
The swapExactTokensForTokens() function needs to be called using the addresses of the ERC-20 contracts. The signature of the function can be found here.
The amount of EURC tokens received as a result of calling the swapExactTokensForTokens() function is matched against the amount contained in the entry of the ReferenceToEurAmount map. If the resulting amount of the swap is higher than what was expected in the entry, the surplus of EURC tokens is sent to the buffer pool account.
If the resulting amount of the swap is less than what was expected in the entry, the pallet tries to transfer the missing amount of tokens from the buffer pool to the pallet account. If the buffer pool does not have enough tokens to cover all of the missing amount, the pallet does not continue processing this transfer but emits a TransferPaused event instead.
Next, the pallet calls the transfer() function of the Tokens pallet to transfer the available EURC on the pallet account to the offramping account and emits a TransferProcessed event. !TBD! this might have to change depending on the EUR issuer
Continuing from paused transfers
This is not part of this ticket but is covered in #378.
Context
The context can be found in this Notion page.
Pallet configuration
Name
forex-automationConfig Items
PalletId- aPalletIdused to derive the on-chain accounts for the automation and the buffer poolStorage Items
NablaRouterAddress- the address of Nabla’sroutersmart contractEurcErc20Address- the address of the ERC-20 wrapper contract for the EURC tokenBrzErc20Address- the address of the ERC-20 wrapper contract for the BRZ tokenOfframpAccount- the address of the designated off-ramping account. !TBD! This might not be required based on the EUR issuerReferenceToEurAmount- a map fromReference -> Balancewhich is used to store the desired target amount of EUR that were initially shown to the user upon creating a transfer request in PendulumPay. We store the amount and not an exchange rate because this way we don't have to incorporate any (on-ramp or XCM) fees that might have been deducted from the BRZ amount until it arrives on our parachain.AuthorizedAccounts- a list of authorized accounts that are allowed to add entries to theReferenceToEurAmountmapExtrinsics
setNablaRouterAddress(address)- sets the address of theroutercontract. Root extrinsicsetErc20Adresses(eurcAddress, brzAddress)- sets the addresses of the erc-20 contracts. Root extrinsicsetOfframpAccount(address)- sets the address of the offramping account. Root extrinsicaddEurAmountForReference(reference, amount)- adds an entry to theReferenceToEurAmount. Only accounts registered inAuthorizedAccountsare allowed to call this extrinsic.addAuthorizedAccount(account)- adds an authorized account toAuthorizedAccounts. Callable from Root and other authorized accountsremoveAuthorizedAccount(account)- removes an authorized account fromAuthorizedAccounts. Callable from Root and other authorized accounts.chargeBufferPoolAccount(currency)- transfers tokens to the pallet's buffer pool account. This is a mere helper function that makes it easy to charge the pool without having to know the address of the buffer pool. Callable from anyone. We don't need to limit the currencies that can be used with this function, but generally, it would only be used to charge the buffer pool with BRZ or EURC tokens.Functions (not exposed as extrinsic but available to other pallets)
processEurcOfframp(amount, reference)- this function is called by the XCM config of our runtime. It handles the conversion from BRZ → EUR and transfers the tokens to the offramping account.amountis the amount of BRZ tokens that are to be offramped andreferenceis the reference that was encoded by theMultiLocationgetPalletAccount()- this function returns the account of this pallet, derived from thePalletIdconfiguration parameter. This can be done with<T as Config>::PalletId::get().into_sub_account_truncating(0)getBufferPoolAccount()- this function returns the account of the pool buffer, derived from thePalletIdconfiguration parameter. This can be done with<T as Config>::PalletId::get().into_sub_account_truncating(1)Events
TransferProcessed(reference, amount, currency)- emitted after sending the swapped tokens to the designated offramping account.TransferPaused(reference, amount, currency)- emitted when the offramp cannot be executed with the targeted amount. This happens if the buffer pool does not have enough funds to cover the rest or if a Nabla swap is too expensive.Coupling to other pallets
Processing the offramp
Prerequisites
Upon receiving an XCM transfer of BRZ from Moonbeam targeting our automation pallet, the XCM config deposits the respective amount of BRZ into this pallet’s on-chain account (returned by
getPalletAccount()) and calls theprocessEurcOfframp()function, supplying the processed amount of BRZ and the reference number (which is encoded in theMultiLocation).Implementation of the
processEurcOfframp(amount, reference)functionThe
processEurcOfframp()function checks if there is an entry for the suppliedreferenceparameter in theReferenceToEurAmountmap. If so, it uses thecall()function of the Contracts pallet to call theswapExactTokensForTokens()function on Nabla’sroutersmart contract to swap theamountof BRZ tokens to EURC tokens. As a result, it will receive the corresponding amount of EURC on its on-chain account.The
swapExactTokensForTokens()function needs to be called using the addresses of the ERC-20 contracts. The signature of the function can be found here.The amount of EURC tokens received as a result of calling the
swapExactTokensForTokens()function is matched against the amount contained in the entry of theReferenceToEurAmountmap. If the resulting amount of the swap is higher than what was expected in the entry, the surplus of EURC tokens is sent to the buffer pool account.If the resulting amount of the swap is less than what was expected in the entry, the pallet tries to transfer the missing amount of tokens from the buffer pool to the pallet account. If the buffer pool does not have enough tokens to cover all of the missing amount, the pallet does not continue processing this transfer but emits a
TransferPausedevent instead.Next, the pallet calls the
transfer()function of the Tokens pallet to transfer the available EURC on the pallet account to the offramping account and emits aTransferProcessedevent. !TBD! this might have to change depending on the EUR issuerContinuing from paused transfers
This is not part of this ticket but is covered in #378.