Cross-Chain Bridge
USPD is designed to be a versatile stablecoin that operates seamlessly across multiple blockchain networks. While USPD is managing the liquidity on Ethereum mainnet, it is planned that USPD is deployed to most, if not all, other EVM chains as well as non-EVM chains. It should be possible to natively bridge the asset between the chains without sacrificing security.
Problem Statement
To enhance USPD’s utility, it needs to be transferable and usable across multiple blockchain networks (Layer 1s and Layer 2s). This presents several challenges:
- Value Preservation: USPD is a yield-bearing stablecoin. Its user-facing value (USPD amount) is derived from underlying, non-rebasing
cUSPDshare tokens and a dynamicyieldFactor(USPD =cUSPDshares *yieldFactor/FACTOR_PRECISION). This relationship must be maintained or accurately translated across chains. - Collateral Integrity: All USPD, regardless of the chain it resides on, is backed by collateral held exclusively on the Ethereum mainnet (L1). The bridging mechanism must ensure that USPD on other chains (L2s) is fully backed by an equivalent value locked on L1.
- Supply Accounting: The
totalSupplyof USPD on L1 should accurately reflect the total system liability against the L1 collateral. Tokens moved to L2s should be accounted for as locked on L1, not burned, to maintain this clarity. - Yield Propagation: The yield generated by the collateral on L1 (which increases the
yieldFactor) should ideally be reflected in the USPD value on L2s, or at least, the bridging mechanism must account foryieldFactordiscrepancies between chains at the time of transfer. - Security: The bridging process must be secure, minimizing trust assumptions and protecting against exploits that could lead to unbacked USPD or loss of user funds.
Solution Architecture Overview
The USPD cross-chain solution uses a lock-and-mint mechanism. To transfer tokens from Ethereum Mainnet (L1) to other chains (L2s/satellites), tokens are locked on L1 and minted on the L2. To return tokens from L2s to L1, tokens are burned on the L2 and unlocked on L1. Smart contracts on L1 and L2s facilitate this process, with off-chain relayers handling message passing.
The BridgeEscrow contract on L1 is central to this design. It manages a global pool of locked cUSPD shares for each destination chain.
When USPD is bridged from L1 to an L2:
- A user interacts with a Token Adapter (e.g., for Wormhole, LayerZero).
- The Token Adapter calls
USPDToken.lockForBridgingon L1. USPDTokencalculates the equivalentcUSPDshares. It then transfers these shares from the Token Adapter to theL1_BridgeEscrowcontract.L1_BridgeEscrowrecords these shares as locked for the specifiedtargetChainId.
To the Token Adapter, this lockForBridging interaction resembles a “burn” operation. The USPD (as cUSPD shares) is removed from the Token Adapter’s control and secured by the BridgeEscrow.
When tokens are bridged from an L2 back to L1:
- L2
cUSPDshares are burned. This is managed by the L2USPDTokenand L2BridgeEscrow. - A message is relayed to L1.
- The L1
USPDToken.unlockFromBridgingfunction is called. This instructsL1_BridgeEscrowto release the previously lockedcUSPDshares to the recipient. For the user, this appears like a “mint” operation on L1.
This global lock-per-chain model in BridgeEscrow allows different bridge providers (Token Adapters) to use a single liquidity and accounting system on L1. This avoids separate locked pools for each bridge.
Core Contracts Involved:
USPDToken.sol(L1 & L2s):- The primary user-facing ERC20 token.
- On L1, it handles conversions between USPD amounts and
cUSPDshare amounts using the L1PoolSharesConversionRate. It initiates bridging operations by interacting with theBridgeEscrowcontract. - On L2s, it provides the USPD view over L2
cUSPDshares, using the L2PoolSharesConversionRate. It initiates bridging back to L1 by interacting with the L2cUSPDToken.
cUSPDToken.sol(L1 & L2s):- The core, non-rebasing ERC20 share token.
- On L1, its shares are transferred to and from the
BridgeEscrowcontract during bridging operations. - On L2s, its shares are minted to users when bridging from L1 and burned when bridging back to L1.
BridgeEscrow.sol(L1 only):- A dedicated contract responsible for holding all
cUSPDshares locked on L1 that back USPD on L2s. - Tracks the total
cUSPDshares bridged out and maintains a per-chain accounting of these shares (bridgedOutSharesPerChain[chainId]). - On L1, it locks/unlocks
cUSPDshares. - On L2s, it facilitates burning/minting of L2
cUSPDshares.
- A dedicated contract responsible for holding all
PoolSharesConversionRate.sol(L1 & L2s):- Provides the
yieldFactorused to convert betweencUSPDshares and USPD amounts. - The L1 instance reflects the true yield from mainnet collateral, calculated from its stETH balance.
- L2 instances store a
_yieldFactorthat is initialized toFACTOR_PRECISION(1:1) and can be updated by an authorizedYIELD_FACTOR_UPDATER_ROLE(typically the L2BridgeEscrow) to reflect the L1yieldFactorreceived during bridge operations. This ensures accurate USPD value representation on L2s.
- Provides the
Key Bridging Principle:
The user’s intent is always to bridge a specific USPD value. This uspdAmountIntended by the user, along with the yieldFactor of the source chain at the time of initiating the bridge, determines a fixed quantity of cUSPD shares. This precise cUSPD share quantity is what is acted upon (locked on L1, minted on L2, burned on L2, or unlocked on L1) across the bridge. The sourceYieldFactor is always included in the cross-chain message to ensure consistent share calculation on the destination chain.
Bridging Process Detailed
1. L1 (Mainnet) to L2 (e.g., Polygon, Arbitrum)
Steps:
- User Interaction: The user specifies the
uspdAmountToBridgeand thetargetChainIdthrough a bridge interface or by interacting with an integrated application. - L1
USPDToken.lockForBridging(uspdAmountToBridge, targetChainId):- User Approval: The user approves a “Token Adapter” contract (e.g., a specific contract for a bridge provider like Wormhole) to spend their L1
USPDToken. - Token Adapter Pulls USPD: The Token Adapter calls
L1_USPDToken.transferFrom(userAddress, address(TokenAdapter), uspdAmountToBridge), moving the USPD from the user to itself. The Token Adapter now knows theuserAddress(original initiator). - Token Adapter Initiates Lock: The Token Adapter (which must have
RELAYER_ROLEonL1_USPDToken) then callsL1_USPDToken.lockForBridging(uspdAmountToBridge, targetChainId).
- User Approval: The user approves a “Token Adapter” contract (e.g., a specific contract for a bridge provider like Wormhole) to spend their L1
- L1
USPDToken.lockForBridging(uspdAmountToBridge, targetChainId):- This function is called by the Token Adapter (
msg.senderis the Token Adapter) and requiresmsg.senderto have theRELAYER_ROLE. - It calculates
cUSPDShareAmountToLock = (uspdAmountToBridge * FACTOR_PRECISION) / currentL1YieldFactor. ThecurrentL1YieldFactoris fetched from the L1PoolSharesConversionRate. - Share Transfer to Escrow:
USPDToken(which hasUSPD_CALLER_ROLEoncUSPDToken) callsL1_cUSPDToken.executeTransfer(address(TokenAdapter), address(L1_BridgeEscrow), cUSPDShareAmountToLock). This transfers thecUSPDshares corresponding to the bridged USPD from the Token Adapter’s balance directly to theL1_BridgeEscrowcontract. - It then calls
L1_BridgeEscrow.escrowShares(cUSPDShareAmountToLock, targetChainId, uspdAmountToBridge, currentL1YieldFactor, address(TokenAdapter)).
- This function is called by the Token Adapter (
- L1
BridgeEscrow.escrowShares(cUSPDShareAmountToLock, targetChainId, uspdAmountIntended, l1YieldFactor, tokenAdapterAddress):- This function on the
BridgeEscrowcontract is called by the respective chain’sUSPDToken. - L1 Behavior:
- The
cUSPDshares are assumed to have already been transferred toBridgeEscrow’s address byL1_USPDToken. - It updates its internal state: increments
totalBridgedOutShares(total L1 shares locked) andbridgedOutSharesPerChain[targetChainId]bycUSPDShareAmountToLock.
- The
- L2 Behavior (Satellite Chain):
- The
cUSPDshares are assumed to have already been transferred toBridgeEscrow’s address byL2_USPDToken. BridgeEscrowcallsL2_cUSPDToken.burn(cUSPDShareAmountToLock)to burn the shares it just received. (RequiresBridgeEscrowto haveBURNER_ROLEon L2cUSPDToken).- It updates its internal state: increments
totalBridgedOutShares(total net shares sent from this L2) andbridgedOutSharesPerChain[targetChainId](net shares sent from this L2 totargetChainId).
- The
- It emits a
SharesLockedForBridgingevent containingtokenAdapterAddress,targetChainId,cUSPDShareAmountToLock,uspdAmountIntended, and the source chain’syieldFactor.
- This function on the
- Off-Chain Bridge Relayer: A relayer service (specific to the chosen bridge provider like Wormhole, LayerZero, etc.) monitors for
SharesLockedForBridgingevents on the source chain’sBridgeEscrow. - Cross-Chain Message to L2:
- The Token Adapter, when initiating the bridge transfer with the bridge provider (e.g., calling Wormhole’s
transferTokens), specifies theoriginalUserAddress(or their L2 equivalent) as the L2 recipient. - The relayer detects the
SharesLockedForBridgingevent. It correlates this event (e.g., bytokenAdapterAddress,targetChainId, amounts) with the message initiated by the Token Adapter. - The relayer constructs and sends the message (or validates/processes the provider’s message) to the target L2. This message includes:
recipientAddress(the original user’s address on L2, as specified by the Token Adapter).
uspdAmountIntended(this is the same asuspdAmountToBridgefrom L1).sourceL1YieldFactor(this is thecurrentL1YieldFactorfrom L1 at the time of locking).
- The Token Adapter, when initiating the bridge transfer with the bridge provider (e.g., calling Wormhole’s
- L2 Bridge Contract: A corresponding bridge contract on the L2 receives and authenticates the message from the relayer.
- L2
cUSPDToken.mintForBridging(recipientAddress, uspdAmountIntended, sourceL1YieldFactor)(or similar entry point):- The authenticated L2 bridge contract calls a designated function on the L2
cUSPDToken(or L2USPDToken, which then interacts with L2cUSPDToken). - This function calculates
cUSPDShareAmountToMintOnL2 = (uspdAmountIntended * FACTOR_PRECISION) / sourceL1YieldFactor. Note that it uses thesourceL1YieldFactorfrom the message to ensure the share amount minted on L2 precisely matches the share amount locked on L1 for that transaction. - It mints
cUSPDShareAmountToMintOnL2to therecipientAddresson the L2. - It emits an event like
SharesMintedFromBridge.
- The authenticated L2 bridge contract calls a designated function on the L2
- L2
PoolSharesConversionRateUpdate: WhenL2_USPDToken.unlockFromBridgingcallsL2_BridgeEscrow.releaseShares, theL2_BridgeEscrow(after minting shares onL2_cUSPDToken) callsL2_PoolSharesConversionRate.updateL2YieldFactor()with thesourceL1YieldFactorreceived in the message. This ensures that the L2 yield factor reflects the L1 factor at the time of the source transaction. (RequiresL2_BridgeEscrowto haveYIELD_FACTOR_UPDATER_ROLEonL2_PoolSharesConversionRate).
2. L2 to L1 (Mainnet)
Steps:
- User Interaction: The user specifies the
uspdAmountToBridgeon the L2 they wish to send back to L1. - L2
USPDToken.burnForBridging(uspdAmountToBridge)(or similar):- This function is called on the L2
USPDTokencontract. - It fetches the
currentL2YieldFactorfrom the L2PoolSharesConversionRate. - It calculates
cUSPDShareAmountToBurn = (uspdAmountToBridge * FACTOR_PRECISION) / currentL2YieldFactor. - It then interacts with the L2
cUSPDTokento burncUSPDShareAmountToBurnfrom the user’s L2 balance. The user must have approved L2USPDTokento manage their L2cUSPDshares, orUSPDTokencalls a specific burn function oncUSPDToken. - An event like
SharesBurnedForBridgingis emitted by L2cUSPDToken(or L2USPDToken) containinguserAddress, the L1 chain ID,cUSPDShareAmountToBurn, the originaluspdAmountToBridge, andcurrentL2YieldFactor.
- This function is called on the L2
- Off-Chain Bridge Relayer: The relayer monitors for
SharesBurnedForBridgingevents on the L2. - Cross-Chain Message to L1: The relayer constructs and sends a message to L1. This message includes:
recipientAddress(the user’s address on L1).uspdAmountIntended(same asuspdAmountToBridgefrom L2).sourceL2YieldFactor(this is thecurrentL2YieldFactorfrom L2 at the time of burning).sourceL2ChainId.
- L1 Bridge Contract: A bridge contract on L1 (or an authorized relayer directly) receives and authenticates the message.
- L1
USPDToken.unlockFromBridging(recipientAddress, uspdAmountIntended, sourceL2YieldFactor, sourceL2ChainId):- The authenticated L1 bridge contract (or an authorized relayer, which must have
RELAYER_ROLEonL1_USPDToken) calls this function on the L1USPDToken. - It calculates
cUSPDShareAmountToUnlock = (uspdAmountIntended * FACTOR_PRECISION) / sourceL2YieldFactor. It uses thesourceL2YieldFactorfrom the message. - It then calls
BridgeEscrow.releaseShares(recipientAddress, cUSPDShareAmountToUnlock, sourceL2ChainId, uspdAmountIntended, sourceL2YieldFactor).
- The authenticated L1 bridge contract (or an authorized relayer, which must have
- L1
BridgeEscrow.releaseShares(recipientAddress, cUSPDShareAmountToUnlock, sourceL2ChainId, uspdAmountIntended, sourceL2YieldFactor):- This function on the
BridgeEscrowcontract handles the “release” or minting of shares on the destination chain. It is called byL1_USPDToken.BridgeEscrowverifies thatmsg.senderis the configureduspdTokenAddress. - L1 Behavior (Receiving from L2):
- It verifies that
cUSPDShareAmountToUnlockcan be released from L1 escrow (i.e.,bridgedOutSharesPerChain[sourceL2ChainId]is sufficient). - It executes
L1_cUSPDToken.transfer(recipientAddress, cUSPDShareAmountToUnlock)to send thecUSPDshares from theBridgeEscrowcontract back to the user on L1. - It updates its internal state: decrements
totalBridgedOutShares(total L1 shares locked) andbridgedOutSharesPerChain[sourceL2ChainId]bycUSPDShareAmountToUnlock.
- It verifies that
- L2 Behavior (Receiving from L1 or another L2):
BridgeEscrowcallsL2_cUSPDToken.mint(recipientAddress, cUSPDShareAmountToUnlock). (RequiresBridgeEscrowto haveMINTER_ROLEon L2cUSPDToken).- It updates its internal state: decrements
totalBridgedOutShares(total net shares sent from this L2) andbridgedOutSharesPerChain[sourceL2ChainId](net shares sent from this L2 tosourceChainId).
- It emits a
SharesUnlockedFromBridgeevent. - The user now holds
cUSPDShareAmountToUnlockon L1. The actual USPD value of these shares is determined by thecurrentL1YieldFactor. If L1 yield has accrued while tokens were on L2, the user effectively receives this accrued yield upon bridging back.
- This function on the
Yield Factor Synchronization on L2s
The accuracy of USPD value representation on L2s depends on the L2 PoolSharesConversionRate reflecting a yieldFactor that is reasonably synchronized with L1.
- Per-Transaction Update: Including the
sourceL1YieldFactorin the bridge message (for L1->L2 transfers) and using it to update the L2PoolSharesConversionRateensures immediate consistency for that batch of tokens. - Periodic Oracle Update: Alternatively, or additionally, a trusted oracle mechanism could periodically read the L1
yieldFactorand update L2PoolSharesConversionRatecontracts. This smooths out theyieldFactoron L2s over time. - Lag: It’s acknowledged that L2
yieldFactormight slightly lag L1’s. However, the core bridging mechanism ensures that the quantity of shares is preserved based on theyieldFactorat the moment of the bridging transaction initiation.
Security Considerations
- Role-Based Access Control (RBAC):
USPDToken.lockForBridging(L1 & L2s) requiresRELAYER_ROLEfor the caller (e.g., Token Adapter).USPDToken.unlockFromBridging(L1 & L2s) requiresRELAYER_ROLEfor the caller (e.g., Token Adapter or Bridge Relayer Contract).BridgeEscrow.escrowSharesandBridgeEscrow.releaseShares(L1 & L2s) require the caller to be the configureduspdTokenAddress.BridgeEscrowhas no owner-protected admin functions after deployment. Its parameters (cUSPDToken,uspdTokenAddress,rateContract) are immutable.BridgeEscrowon L2s requiresMINTER_ROLEandBURNER_ROLEon the L2cUSPDToken.BridgeEscrowon L2s requiresYIELD_FACTOR_UPDATER_ROLEon the L2PoolSharesConversionRate.- Other sensitive functions (
mintForBridgingin L2cUSPDToken;updateL2YieldFactorin L2PoolSharesConversionRate) must be protected by robust RBAC.
- Bridge Relayer Security: The security of the chosen off-chain bridge relayer system is paramount. Compromised relayers could submit fraudulent messages.
- Message Authentication: Cross-chain messages must be rigorously authenticated on the destination chain to prevent spoofing.
- Per-Chain Limits: Per-chain limits are not enforced by the
BridgeEscrowcontract itself. This responsibility is delegated to the Token Adapter contracts, which will only be grantedRELAYER_ROLEonUSPDTokenif they implement appropriate velocity/limit controls. - Reentrancy Guards: Apply reentrancy guards where appropriate, especially in functions involving external calls and state changes.
- Audits: Thorough independent security audits of all involved smart contracts and the overall bridging architecture are essential.
- Emergency Pausability: Consider pausable mechanisms for critical functions in case of detected vulnerabilities.
Implementation Plan
This plan outlines the smart contract development and modifications required.
1. BridgeEscrow.sol (New Contract - L1 Only)
- State Variables:
IERC20 public immutable cUSPDToken;address public owner;(orAccessControlfor admin roles)uint256 public totalBridgedOutShares;mapping(uint256 => uint256) public bridgedOutSharesPerChain; // chainId => sharesAmountmapping(address => bool) public authorizedRelayers; // For releaseSharesaddress public uspdTokenAddress; // To verify caller of escrowShares
- Events:
event SharesLockedForBridging(address indexed tokenAdapter, uint256 indexed targetChainId, uint256 cUSPDShareAmount, uint256 uspdAmountIntended, uint256 l1YieldFactor);event SharesUnlockedFromBridge(address indexed recipient, uint256 indexed sourceChainId, uint256 cUSPDShareAmount, uint256 uspdAmountIntended, uint256 l2YieldFactor);
- Functions:
constructor(address _cUSPDTokenAddress, address _uspdTokenAddress, address _rateContractAddress): Sets immutable addresses. No admin/owner.escrowShares(uint256 cUSPDShareAmount, uint256 targetChainId, uint256 uspdAmountIntended, uint256 sourceYieldFactor, address tokenAdapter):require(msg.sender == uspdTokenAddress, "Caller not USPDToken");- If
block.chainid == MAINNET_CHAIN_ID: Accounts for shares already transferred to it. - If
block.chainid != MAINNET_CHAIN_ID: CallscUSPDToken.burn(cUSPDShareAmount)on shares already transferred to it. - Updates
totalBridgedOutSharesandbridgedOutSharesPerChain[targetChainId]. - Emits
SharesLockedForBridging.
releaseShares(address recipient, uint256 cUSPDShareAmount, uint256 sourceChainId, uint256 uspdAmountIntended, uint256 sourceYieldFactor):require(msg.sender == uspdTokenAddress, "Caller not USPDToken");- If
block.chainid == MAINNET_CHAIN_ID: TransferscUSPDTokenfrom self to recipient. - If
block.chainid != MAINNET_CHAIN_ID: CallscUSPDToken.mint(recipient, cUSPDShareAmount), then callsrateContract.updateL2YieldFactor(sourceYieldFactor). - Updates
totalBridgedOutSharesandbridgedOutSharesPerChain[sourceChainId]. - Emits
SharesUnlockedFromBridge.
- Admin Functions: None. The contract is not ownable and has no admin-modifiable state post-deployment.
// *
setUspdTokenAddress(address _uspdTokenAddress)// Removed // *setChainLimit(uint256 chainId, uint256 limit)// Removed // *transferOwnership(address newOwner)// Removed // *withdrawERC20(...)// Removed // *withdrawETH(...)// Removed (replaced by reverting receive())
2. USPDToken.sol Modifications (L1)
- State Variables:
address public bridgeEscrowAddress;
- Events (Consider if new events are needed here or if
BridgeEscrowevents suffice):LockForBridgingInitiated(address indexed tokenAdapter, uint256 targetChainId, uint256 uspdAmount, uint256 cUSPDShareAmount);UnlockFromBridgingInitiated(address indexed recipient, uint256 sourceChainId, uint256 uspdAmountIntended, uint256 sourceChainYieldFactor, uint256 cUSPDShareAmount);
- Functions:
lockForBridging(uint256 uspdAmountToBridge, uint256 targetChainId):- Called by the Token Adapter/Relayer (
msg.sender). Requiresmsg.senderto haveRELAYER_ROLE. - Calls
cUSPDToken.executeTransfer(...)to move shares from Token Adapter toBridgeEscrow. - Calls
BridgeEscrow.escrowShares(...). - Emits
LockForBridgingInitiated.
- Called by the Token Adapter/Relayer (
unlockFromBridging(address recipient, uint256 uspdAmountIntended, uint256 sourceChainYieldFactor, uint256 sourceChainId):- Called by an authorized Relayer (
msg.sender). Requiresmsg.senderto haveRELAYER_ROLE. - Calculates
cUSPDShareAmountToUnlock. - Calls
BridgeEscrow.releaseShares(...). (BridgeEscrowverifies caller isUSPDToken). - Emits
UnlockFromBridgingInitiated.
- Called by an authorized Relayer (
- Admin Functions:
setBridgeEscrowAddress(address _bridgeEscrowAddress)(onlyRoleDEFAULT_ADMIN_ROLE)grantRole(RELAYER_ROLE, address relayerOrAdapterAddress)(onlyRoleDEFAULT_ADMIN_ROLE)revokeRole(RELAYER_ROLE, address relayerOrAdapterAddress)(onlyRoleDEFAULT_ADMIN_ROLE)
3. cUSPDToken.sol Modifications (L2)
- State Variables (Consider if specific roles are needed for bridge minters):
mapping(address => bool) public authorizedBridgeMinters;
- Events:
SharesMintedFromBridge(address indexed recipient, uint256 cUSPDShareAmount, uint256 uspdAmountIntended, uint256 sourceL1YieldFactor);
- Functions:
mintForBridging(address recipient, uint256 uspdAmountIntended, uint256 sourceL1YieldFactor):require(authorizedBridgeMinters[msg.sender], "Caller not bridge minter");- Calculates
cUSPDShareAmountToMint = (uspdAmountIntended * FACTOR_PRECISION) / sourceL1YieldFactor. _mint(recipient, cUSPDShareAmountToMint).- Emits
SharesMintedFromBridge.
- Admin Functions:
setAuthorizedBridgeMinter(address minter, bool isAuthorized)(onlyRoleDEFAULT_ADMIN_ROLE) // This role is for BridgeEscrow on L2
4. USPDToken.sol Modifications (L2)
- State Variables (Consider if specific roles are needed for bridge burners):
address public cUSPDTokenAddress_L2;
- Events:
SharesBurnedForBridging(address indexed burner, uint256 targetChainId, uint256 cUSPDShareAmount, uint256 uspdAmountIntended, uint256 l2YieldFactor);
- Functions:
burnForBridging(uint256 uspdAmountToBridge):- Requires L2
rateContractandcUSPDTokenAddress_L2to be set. - Calculates
cUSPDShareAmountToBurnusing L2rateContract.getYieldFactor(). - Calls
IcUSPDToken(cUSPDTokenAddress_L2).burn(cUSPDShareAmountToBurn). (Requires user to have approved L2USPDTokento burn their L2cUSPDshares, orUSPDTokencallsexecuteBurnoncUSPDToken). - Emits
SharesBurnedForBridging.
- Requires L2
- Admin Functions:
setCUSPDTokenAddress(address _address)setRateContractAddress(address _address)
5. PoolSharesConversionRate.sol Modifications (L2)
- Functions:
constructor(address _stETHAddress, address _lidoAddress, address _admin): On L1, stakes ETH. On L2, initializes_yieldFactortoFACTOR_PRECISION. Grants_adminDEFAULT_ADMIN_ROLEandYIELD_FACTOR_UPDATER_ROLE(on L2).getYieldFactor(): Returns calculated factor on L1, stored_yieldFactoron L2.updateL2YieldFactor(uint256 newYieldFactor): Callable on L2 byYIELD_FACTOR_UPDATER_ROLE. Updates_yieldFactorifnewYieldFactoris not smaller.
6. Deployment & Configuration Steps:
- Deploy
PoolSharesConversionRateon L1 (with ETH deposit) and L2s (no deposit), passing admin address. - Deploy
BridgeEscrowcontract on L1 and each L2, providing respective chain’scUSPDTokenaddress,USPDTokenaddress, andPoolSharesConversionRateaddress. - Call
USPDToken.setBridgeEscrowAddress()on L1 and each L2 with their respective deployedBridgeEscrowaddress. - Grant
RELAYER_ROLEon eachUSPDTokeninstance to the authorized relayer/Token Adapter contract addresses. - On L2s, grant
MINTER_ROLEandBURNER_ROLEon L2cUSPDTokento the L2BridgeEscrowaddress. - On L2s, grant
YIELD_FACTOR_UPDATER_ROLEon L2PoolSharesConversionRateto the L2BridgeEscrowaddress. - Deploy L2 versions of
USPDTokenandcUSPDToken. - Configure L2
USPDTokenwith L2cUSPDTokenand L2PoolSharesConversionRateaddresses.
7. Off-Chain Components:
- Develop or integrate bridge relayer services compatible with the chosen cross-chain messaging protocol (e.g., Wormhole, LayerZero, Axelar). These relayers must:
- Monitor L1
BridgeEscrowforSharesLockedForBridgingevents. - Monitor L2
USPDToken(orcUSPDToken) forSharesBurnedForBridgingevents. - Securely construct and transmit messages to the destination chain.
- Monitor L1
- Develop a user-friendly bridge interface (UI).
This detailed plan should serve as a solid guideline for development and subsequent audits. Stay tuned for more updates as we progress with the development of this feature.