Cryo Explorer Ethereum Mainnet

Address Contract Verified

Address 0x42458259d5c85fB2bf117f197f1Fef8C3b7dCBfe
Balance 0.568737 ETH ($1125.70)
Nonce 1
Code Size 8718 bytes
Indexed Transactions Index loading...
External Etherscan · Sourcify

Contract Bytecode

8718 bytes
0x6080604052600436106100e7575f3560e01c806376369b6d11610087578063a45c265a11610057578063a45c265a14610224578063c290d69114610243578063e52eb1e314610262578063f3fef3a314610281575f5ffd5b806376369b6d146101b65780637a90b990146101dd5780637c960ff4146101f15780638456cb5914610210575f5ffd5b80633f4ba83a116100c25780633f4ba83a14610145578063668cec3b146101595780636944af66146101785780636f48595a14610197575f5ffd5b8063035a2875146100f25780632f14b9e6146101135780632f820a5f14610132575f5ffd5b366100ee57005b5f5ffd5b3480156100fd575f5ffd5b5061011161010c366004611e6f565b6102a0565b005b34801561011e575f5ffd5b5061011161012d366004611e6f565b610360565b610111610140366004611ed4565b61040c565b348015610150575f5ffd5b5061011161080c565b348015610164575f5ffd5b50610111610173366004611f07565b610896565b348015610183575f5ffd5b50610111610192366004611f07565b61094b565b3480156101a2575f5ffd5b506101116101b1366004611e6f565b6109f6565b3480156101c1575f5ffd5b506101cb600b5481565b60405190815260200160405180910390f35b3480156101e8575f5ffd5b50610111610aab565b3480156101fc575f5ffd5b5061011161020b366004611f62565b610b30565b34801561021b575f5ffd5b50610111610b68565b34801561022f575f5ffd5b5061011161023e366004611f07565b610bf1565b34801561024e575f5ffd5b5061011161025d366004611f9c565b610ca5565b34801561026d575f5ffd5b5061011161027c366004611fb3565b610cdd565b34801561028c575f5ffd5b5061011161029b366004612007565b610d29565b335f9081526001602052604090205460ff166102d75760405162461bcd60e51b81526004016102ce9061202f565b60405180910390fd5b60025487906001600160a01b031615806102fe57506002546001600160a01b038281169116145b61031a5760405162461bcd60e51b81526004016102ce9061205f565b6040805160a081018252878152602081018790529081018590526001600160a01b038085166060830152831660808201526103558882610f18565b505050505050505050565b335f9081526001602052604090205460ff1661038e5760405162461bcd60e51b81526004016102ce9061202f565b60025487906001600160a01b031615806103b557506002546001600160a01b038281169116145b6103d15760405162461bcd60e51b81526004016102ce9061205f565b6040805160a081018252878152602081018790529081018590526001600160a01b038085166060830152831660808201526103558882610f61565b600354600160a01b900460ff1661045c5760405162461bcd60e51b81526020600482015260146024820152734e6f7420616374697665207269676874206e6f7760601b60448201526064016102ce565b5f600b543a61046b91906120a3565b9050803410156104f45760405162461bcd60e51b815260206004820152604860248201527f496e73756666696369656e7420666565207061696420666f722062726964676960448201527f6e67202d20706179206174206c656173742074782e6761732074696d6573206760648201526761735f666565282960c01b608482015260a4016102ce565b803411156105b2575f3361050883346120ba565b604080515f81526020810191829052610520916120cd565b5f6040518083038185875af1925050503d805f811461055a576040519150601f19603f3d011682016040523d82523d5f602084013e61055f565b606091505b50509050806105b05760405162461bcd60e51b815260206004820152601f60248201527f556e61626c6520746f2072657475726e2074686520657863657373206665650060448201526064016102ce565b505b600a546040516370a0823160e01b815233600482015283916001600160a01b0316906370a0823190602401602060405180830381865afa1580156105f8573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061061c91906120e3565b101561063a5760405162461bcd60e51b81526004016102ce906120fa565b600a54604051636eb1769f60e11b815233600482015230602482015283916001600160a01b03169063dd62ed3e90604401602060405180830381865afa158015610686573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106aa91906120e3565b10156106f85760405162461bcd60e51b815260206004820152601b60248201527f496e73756666696369656e7420617070726f7665642066756e6473000000000060448201526064016102ce565b8161070283610fb0565b1061074f5760405162461bcd60e51b815260206004820152601b60248201527f4e6f7420656e6f75676820746f20636f7665722074686520666565000000000060448201526064016102ce565b600a546040516323b872dd60e01b8152336004820152306024820152604481018490526001600160a01b03909116906323b872dd906064016020604051808303815f875af11580156107a3573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107c79190612126565b505f6040518060a001604052808681526020015a8152602081018590523360408201526001600160a01b038616606090910152905061080581610fdf565b5050505050565b6003546001600160a01b031633146108365760405162461bcd60e51b81526004016102ce9061214c565b600354600160a01b900460ff16156108815760405162461bcd60e51b815260206004820152600e60248201526d416c72656164792061637469766560901b60448201526064016102ce565b6003805460ff60a01b1916600160a01b179055565b335f9081526001602052604090205460ff166108c45760405162461bcd60e51b81526004016102ce9061202f565b60025486906001600160a01b031615806108eb57506002546001600160a01b038281169116145b6109075760405162461bcd60e51b81526004016102ce9061205f565b6040805160a081018252878152602081018790529081018590526001600160a01b0380851660608301528316608082015261094181611079565b5050505050505050565b335f9081526001602052604090205460ff166109795760405162461bcd60e51b81526004016102ce9061202f565b60025486906001600160a01b031615806109a057506002546001600160a01b038281169116145b6109bc5760405162461bcd60e51b81526004016102ce9061205f565b6040805160a081018252878152602081018790529081018590526001600160a01b0380851660608301528316608082015261094181611118565b335f9081526001602052604090205460ff16610a245760405162461bcd60e51b81526004016102ce9061202f565b60025487906001600160a01b03161580610a4b57506002546001600160a01b038281169116145b610a675760405162461bcd60e51b81526004016102ce9061205f565b6040805160a081018252878152602081018790529081018590526001600160a01b0380851660608301528316608082015261035588610aa5816111b7565b836111e9565b5f80546040516326db15bb60e21b81523060048201526001600160a01b0390911690639b6c56ec90602401602060405180830381865afa158015610af1573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b1591906120e3565b5f54909150610b2d906001600160a01b03168261126e565b50565b6040805160a081018252858152602081018590529081018290523360608201526001600160a01b03831660808201526108058161133f565b6003546001600160a01b03163314610b925760405162461bcd60e51b81526004016102ce9061214c565b600354600160a01b900460ff16610be25760405162461bcd60e51b81526020600482015260146024820152734e6f7420616374697665207269676874206e6f7760601b60448201526064016102ce565b6003805460ff60a01b19169055565b335f9081526001602052604090205460ff16610c1f5760405162461bcd60e51b81526004016102ce9061202f565b60025486906001600160a01b03161580610c4657506002546001600160a01b038281169116145b610c625760405162461bcd60e51b81526004016102ce9061205f565b6040805160a081018252878152602081018790529081018590526001600160a01b03808516606083015283166080820152610941610c9f886111b7565b82611434565b335f9081526001602052604090205460ff16610cd35760405162461bcd60e51b81526004016102ce9061202f565b610b2d338261126e565b6040805160a081018252878152602081018790529081018390526001600160a01b03808616606083015284166080820152610d20610d1a836111b7565b826114cf565b50505050505050565b6003546001600160a01b03163314610d535760405162461bcd60e51b81526004016102ce9061214c565b6001600160a01b038216610e205780471015610d815760405162461bcd60e51b81526004016102ce906120fa565b604080515f8082526020820190925233908390604051610da191906120cd565b5f6040518083038185875af1925050503d805f8114610ddb576040519150601f19603f3d011682016040523d82523d5f602084013e610de0565b606091505b5050905080610e1b5760405162461bcd60e51b81526020600482015260076024820152664661696c75726560c81b60448201526064016102ce565b505050565b6040516370a0823160e01b815230600482015281906001600160a01b038416906370a0823190602401602060405180830381865afa158015610e64573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e8891906120e3565b1015610ea65760405162461bcd60e51b81526004016102ce906120fa565b60405163a9059cbb60e01b8152336004820152602481018290526001600160a01b0383169063a9059cbb906044016020604051808303815f875af1158015610ef0573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e1b9190612126565b5050565b5f60095f610f258461154b565b81526020019081526020015f209050610f4081846001611594565b15610e1b57610f588163cafebabe600360025f6115f6565b610e1b826116bb565b5f60085f610f6e8461154b565b81526020019081526020015f209050610f8881845f611594565b15610fa657610f9c818460028060016115f6565b610e1b838361175a565b610e1b83836117c9565b5f61271060055483610fc291906120a3565b610fcc9190612174565b600454610fd99190612193565b92915050565b5f60085f610fec8461154b565b815260208101919091526040015f9081209150815460ff166004811115611015576110156121a6565b146110625760405162461bcd60e51b815260206004820152601960248201527f5b534d5d204475706c6963617465206d6573736167652049440000000000000060448201526064016102ce565b611070815f6002805f6115f6565b610f148261182c565b5f60085f6110868461154b565b815260208101919091526040015f2090506002815460ff1660048111156110af576110af6121a6565b146110fc5760405162461bcd60e51b815260206004820152601860248201527f5b5044525d20496e76616c6964206d657373616765204944000000000000000060448201526064016102ce565b61110f8163deadbeef600160025f6115f6565b610f1482611893565b5f60085f6111258461154b565b815260208101919091526040015f2090506002815460ff16600481111561114e5761114e6121a6565b1461119b5760405162461bcd60e51b815260206004820152601860248201527f5b5044435d20496e76616c6964206d657373616765204944000000000000000060448201526064016102ce565b6111ae8163beefcafe600460025f6115f6565b610f148261199e565b5f816040516020016111cb91815260200190565b60408051601f19818403018152919052805160209091012092915050565b5f60095f6111f68461154b565b81526020019081526020015f20905061121181856001611594565b1561126857611225818560028060016115f6565b600654815460ff9182166101009091049091161061125e576112508163cafebabe600360025f6115f6565b61125982611aec565b611268565b6112688383611c55565b50505050565b8047101561128e5760405162461bcd60e51b81526004016102ce906120fa565b8015610f1457604080515f808252602082019092526001600160a01b0384169083906040516112bd91906120cd565b5f6040518083038185875af1925050503d805f81146112f7576040519150601f19603f3d011682016040523d82523d5f602084013e6112fc565b606091505b5050905080610e1b5760405162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8819985a5b1959608a1b60448201526064016102ce565b600654610100900460ff166113965760405162461bcd60e51b815260206004820152601960248201527f43616e63656c6c6174696f6e73206e6f7420616c6c6f7765640000000000000060448201526064016102ce565b5f60085f6113a38461154b565b815260208101919091526040015f2090506002815460ff1660048111156113cc576113cc6121a6565b1480156113e8575060075460018201546113e690436120ba565b115b6110fc5760405162461bcd60e51b815260206004820152601760248201527f5b434d5d20496e76616c6964206d65737361676520494400000000000000000060448201526064016102ce565b5f60095f6114418461154b565b815260208101919091526040015f9081209150815460ff16600481111561146a5761146a6121a6565b146114b75760405162461bcd60e51b815260206004820152601a60248201527f5b50494d5d204475706c6963617465206d65737361676520494400000000000060448201526064016102ce565b6114c5815f6002805f6115f6565b610e1b8383611c55565b5f60095f6114dc8461154b565b815260208101919091526040015f2090506002815460ff166004811115611505576115056121a6565b146114c55760405162461bcd60e51b815260206004820152601660248201527516d49748125b9d985b1a59081b595cdcd859d948125160521b60448201526064016102ce565b5f815f01518260400151836060015184608001516040516020016111cb949392919093845260208401929092526001600160a01b03908116604084015216606082015260800190565b5f6002845460ff1660048111156115ad576115ad6121a6565b1480156115ee57508160028111156115c7576115c76121a6565b5f848152600280870160205260409091205460ff16908111156115ec576115ec6121a6565b145b949350505050565b5f848152600286810160205260409091205460ff168181111561161b5761161b6121a6565b14610805575f848152600280870160205260409091208054849260ff19909116906001908490811115611650576116506121a6565b021790555084548390869060ff19166001836004811115611673576116736121a6565b02179055504360018601558015610805578454859060019061169c90610100900460ff166121ba565b91906101000a81548160ff021916908360ff1602179055505050505050565b5f60095f6116c88461154b565b815260208101919091526040015f2090506003815460ff1660048111156116f1576116f16121a6565b1461173e5760405162461bcd60e51b815260206004820152601760248201527f5b52445d20496e76616c6964206d65737361676520494400000000000000000060448201526064016102ce565b6117518163deadbeef600160025f6115f6565b610f1482611c9a565b8060200151815f0151837f82a6f338b2afeb24efc15f5a68e65a31960ce16b4581a1894be8f4547a6a69928460400151856060015186608001516040516117bd939291909283526001600160a01b03918216602084015216604082015260600190565b60405180910390a45050565b8060200151815f0151837f12f9401f332d409c70cbc2009ee74b1b2473605e711adab4afa955982b26922e8460400151856060015186608001516040516117bd939291909283526001600160a01b03918216602084015216604082015260600190565b80604001518160200151825f01517f8c89ba8c334cb1a0c05b1099dbb401cb818564b8e10b81d0ae78c1c512fda8c0846060015185608001516040516118889291906001600160a01b0392831681529116602082015260400190565b60405180910390a450565b604081810151600a5491516370a0823160e01b815230600482015290916001600160a01b0316906370a0823190602401602060405180830381865afa1580156118de573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061190291906120e3565b10156119205760405162461bcd60e51b81526004016102ce906120fa565b600a546060820151604080840151905163a9059cbb60e01b81526001600160a01b039283166004820152602481019190915291169063a9059cbb906044016020604051808303815f875af115801561197a573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f149190612126565b604081810151600a5491516370a0823160e01b815230600482015290916001600160a01b0316906370a0823190602401602060405180830381865afa1580156119e9573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a0d91906120e3565b1015611a2b5760405162461bcd60e51b81526004016102ce906120fa565b600a54600160a01b900460ff1615610b2d57600a54600160a81b900460ff1615611aaf57600a546040808301519051630852cd8d60e31b81526001600160a01b03909216916342966c6891611a869160040190815260200190565b5f604051808303815f87803b158015611a9d575f5ffd5b505af1158015610805573d5f5f3e3d5ffd5b600a546040828101519051632770a7eb60e21b815230600482015260248101919091526001600160a01b0390911690639dc29fac90604401611a86565b600a54600160a01b900460ff1615611ba057600a54608082015160408301516001600160a01b03909216916340c10f199190611b2790610fb0565b8460400151611b3691906120ba565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044015f604051808303815f87803b158015611b79575f5ffd5b505af1925050508015611b8a575060015b611b9757610b2d816116bb565b610b2d81611cf6565b600a54608082015160408301516001600160a01b039092169163a9059cbb9190611bc990610fb0565b8460400151611bd891906120ba565b6040516001600160e01b031960e085901b1681526001600160a01b03909216600483015260248201526044016020604051808303815f875af1925050508015611c3e575060408051601f3d908101601f19168201909252611c3b91810190612126565b60015b611c4b57610b2d816116bb565b50610b2d81611cf6565b5f60095f611c628461154b565b81526020019081526020015f209050611c7c81845f611594565b15610e1b57611c908184600260015f6115f6565b610e1b8383611d95565b80604001518160200151825f01517f2345a5b1451246d78b6e58ce955817f26b2b57784f56aa04c95395f4d275d675846060015185608001516040516118889291906001600160a01b0392831681529116602082015260400190565b5f60095f611d038461154b565b815260208101919091526040015f2090506003815460ff166004811115611d2c57611d2c6121a6565b14611d795760405162461bcd60e51b815260206004820152601760248201527f5b43445d20496e76616c6964206d65737361676520494400000000000000000060448201526064016102ce565b611d8c8163beefcafe600460025f6115f6565b610f1482611df8565b8060200151815f0151837f0e668991dc806b96bad9c4cea098fe1f66697f2b765d9628556fa06a5e3ffeb58460400151856060015186608001516040516117bd939291909283526001600160a01b03918216602084015216604082015260600190565b80604001518160200151825f01517fe56d80fb0ff2f929966227056120d5fee57f98d0b057c1b4fb9286f03eeba057846060015185608001516040516118889291906001600160a01b0392831681529116602082015260400190565b80356001600160a01b0381168114611e6a575f5ffd5b919050565b5f5f5f5f5f5f5f60e0888a031215611e85575f5ffd5b611e8e88611e54565b965060208801359550604088013594506060880135935060808801359250611eb860a08901611e54565b9150611ec660c08901611e54565b905092959891949750929550565b5f5f5f60608486031215611ee6575f5ffd5b83359250611ef660208501611e54565b929592945050506040919091013590565b5f5f5f5f5f5f60c08789031215611f1c575f5ffd5b611f2587611e54565b9550602087013594506040870135935060608701359250611f4860808801611e54565b9150611f5660a08801611e54565b90509295509295509295565b5f5f5f5f60808587031215611f75575f5ffd5b8435935060208501359250611f8c60408601611e54565b9396929550929360600135925050565b5f60208284031215611fac575f5ffd5b5035919050565b5f5f5f5f5f5f60c08789031215611fc8575f5ffd5b8635955060208701359450611fdf60408801611e54565b9350611fed60608801611e54565b9598949750929560808101359460a0909101359350915050565b5f5f60408385031215612018575f5ffd5b61202183611e54565b946020939093013593505050565b602080825260169082015275417574686f72697a65642073656e646572206f6e6c7960501b604082015260600190565b602080825260169082015275417574686f72697a65642052564d204944206f6e6c7960501b604082015260600190565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417610fd957610fd961208f565b81810381811115610fd957610fd961208f565b5f82518060208501845e5f920191825250919050565b5f602082840312156120f3575f5ffd5b5051919050565b602080825260129082015271496e73756666696369656e742066756e647360701b604082015260600190565b5f60208284031215612136575f5ffd5b81518015158114612145575f5ffd5b9392505050565b6020808252600e908201526d139bdd08185d5d1a1bdc9a5e995960921b604082015260600190565b5f8261218e57634e487b7160e01b5f52601260045260245ffd5b500490565b80820180821115610fd957610fd961208f565b634e487b7160e01b5f52602160045260245ffd5b5f60ff821660ff81036121cf576121cf61208f565b6001019291505056fea2646970667358221220a450eb9b623b7845c3d7c59a91cc07b5b7831742762452de2037904c097b21db64736f6c634300081c0033

Verified Source Code Full Match

Compiler: v0.8.28+commit.7893614a EVM: cancun Optimization: Yes (200 runs)
Bridge.sol 452 lines
// SPDX-License-Identifier: GPL-2.0-or-later

pragma solidity >=0.8.0;

import '../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol';
import '../lib/reactive-lib/src/abstract-base/AbstractCallback.sol';
import './AbstractDispenser.sol';
import './AbstractFeeCalculator.sol';
import './AbstractBridgehead.sol';
import './BridgeLib.sol';

/// @title Interface for extended ERC20 tokens used by the ERC20/REACT bridge.
interface IERC20ForciblyMintableBurnable is IERC20 {
    function mint(address to, uint256 amount) external;
    function burn(uint256 amount) external;
    function burn(address from, uint256 amount) external;
}

/// @title Implements the ERC20/non-reactive part of the bridge using the abstract protocol defined in `AbstractBridgehead`.
/// @dev The transport to the other part of the bridge is implemented as log records intercepted by the Reactive Network and delivered to the reactive contract on the other side.
contract Bridge is AbstractCallback, AbstractDispenser, AbstractFeeCalculator, AbstractBridgehead {
    event InitialMessage(
        uint256 indexed tx,
        uint256 indexed index,
        uint256 indexed amount,
        address sender,
        address recipient
    );

    event Confirmation(
        uint256 indexed rq,
        uint256 indexed tx,
        uint256 indexed index,
        uint256 amount,
        address sender,
        address recipient
    );

    event Rejection(
        uint256 indexed rq,
        uint256 indexed tx,
        uint256 indexed index,
        uint256 amount,
        address sender,
        address recipient
    );

    event ConfirmationRequest(
        uint256 indexed rq,
        uint256 indexed tx,
        uint256 indexed index,
        uint256 amount,
        address sender,
        address recipient
    );

    event DeliveryConfirmation(
        uint256 indexed tx,
        uint256 indexed index,
        uint256 indexed amount,
        address sender,
        address recipient
    );

    event DeliveryRejection(
        uint256 indexed tx,
        uint256 indexed index,
        uint256 indexed amount,
        address sender,
        address recipient
    );

    /// @notice Address of the Wrapped REACT, or any other token to be bridged.
    IERC20ForciblyMintableBurnable wreact;

    /// @notice Indicated whether `wreact` can be minted. Will lock the incoming tokens otherwise.
    bool is_mintable;

    /// @notice Indicated whether token burning should be through `burn(uint256)` or `burn(address,uint256)` method.
    bool is_standard_burn;

    /// @notice The amount of extra gas the bridging party will pay for to account for confirmation callbacks.
    uint256 public gas_fee;

    constructor(
        address _callback_proxy,
        uint8 _confirmations,
        bool _allow_cancellations,
        uint256 _cancellation_threshold,
        IERC20ForciblyMintableBurnable _wreact,
        bool _is_mintable,
        bool _is_standard_burn,
        uint256 _fixed_fee,
        uint256 _perc_fee,
        uint256 _gas_fee
    ) AbstractCallback(
        _callback_proxy
    ) AbstractFeeCalculator(
        _fixed_fee,
        _perc_fee
    ) AbstractBridgehead(
        _confirmations,
        _allow_cancellations,
        _cancellation_threshold
    ) payable {
        wreact = _wreact;
        is_mintable = _is_mintable;
        is_standard_burn = _is_standard_burn;
        gas_fee = _gas_fee;
    }

    // Outbox methods

    /// @notice Initiate the bridging sequence from ERC20 to native REACT.
    /// @param uniqueish A reasonably unique-ish number identifying this transaction, provided by the client. Should be unique across messages with the same sender-recipient-amount combination.
    /// @param recipient Recipient's addres on the Reactive Network side.
    /// @param amount Amount of ERC20 to be bridged. 1-to-1 minus the bridging fee.
    function bridge(uint256 uniqueish, address recipient, uint256 amount) external payable onlyActive {
        uint256 extra_gas_price = tx.gasprice * gas_fee;
        require(msg.value >= extra_gas_price, 'Insufficient fee paid for bridging - pay at least tx.gas times gas_fee()');
        if (msg.value > extra_gas_price) {
            (bool success,) = payable(msg.sender).call{ value: msg.value - extra_gas_price }(new bytes(0));
            require(success, 'Unable to return the excess fee');
        }
        require(wreact.balanceOf(msg.sender) >= amount, 'Insufficient funds');
        require(wreact.allowance(msg.sender, address(this)) >= amount, 'Insufficient approved funds');
        require(_computeFee(amount) < amount, 'Not enough to cover the fee');
        wreact.transferFrom(msg.sender, address(this), amount);
        MessageId memory message = MessageId({
            tx: uniqueish,
            index: gasleft(),
            amount: amount,
            sender: msg.sender,
            recipient: recipient
        });
        _sendMessage(message);
    }

    /// @notice Cancels the previously sent briging request, if allowed and possible.
    /// @param uniqueish A reasonably unique-ish number identifying this transaction, provided by the client. Must be unique across messages with the same sender-recipient-amount combination.
    /// @param index `gasleft()` at the point where the original message ID has been computed.
    /// @param recipient Recipient's addres on the Reactive Network side.
    /// @param amount Amount of ERC20 to be bridged. 1-to-1 minus the bridging fee.
    function cancel(uint256 uniqueish, uint256 index, address recipient, uint256 amount) external {
        MessageId memory message = MessageId({
            tx: uniqueish,
            index: index,
            amount: amount,
            sender: msg.sender,
            recipient: recipient
        });
        _cancelMessage(message);
    }

    // Outbox callbacks

    /// @notice Entry point for the confirmation requests received as callback transactions from the reactive part of the bridge.
    /// @param rvm_id RVM ID (i.e., reactive contract's deployer address) injected by the reactive node.
    /// @param submsg_id ID of the confirmation request.
    /// @param txh Original unique-ish number identifying the message.
    /// @param index `gasleft()` at the moment the original message ID has been computed.
    /// @param amount Amount sent.
    /// @param sender Sender address.
    /// @param recipient Recipient address.
    function requestConfirmation(
        address rvm_id,
        uint256 submsg_id,
        uint256 txh,
        uint256 index,
        uint256 amount,
        address sender,
        address recipient
    ) external authorizedSenderOnly rvmIdOnly(rvm_id) {
        MessageId memory message = MessageId({
            tx: txh,
            index: index,
            amount: amount,
            sender: sender,
            recipient: recipient
        });
        _processConfirmationRequest(submsg_id, message);
    }

    /// @notice Entry point for the delivery confirmations received as callback transactions from the reactive part of the bridge.
    /// @param rvm_id RVM ID (i.e., reactive contract's deployer address) injected by the reactive node.
    /// @param txh Original unique-ish number identifying the message.
    /// @param index `gasleft()` at the moment the original message ID has been computed.
    /// @param amount Amount sent.
    /// @param sender Sender address.
    /// @param recipient Recipient address.
    function confirmDelivery(
        address rvm_id,
        uint256 txh,
        uint256 index,
        uint256 amount,
        address sender,
        address recipient
    ) external authorizedSenderOnly rvmIdOnly(rvm_id) {
        MessageId memory message = MessageId({
            tx: txh,
            index: index,
            amount: amount,
            sender: sender,
            recipient: recipient
        });
        _processDeliveryConfirmation(message);
    }

    /// @notice Entry point for the delivery rejections received as callback transactions from the reactive part of the bridge.
    /// @param rvm_id RVM ID (i.e., reactive contract's deployer address) injected by the reactive node.
    /// @param txh Original unique-ish number identifying the message.
    /// @param index `gasleft()` at the moment the original message ID has been computed.
    /// @param amount Amount sent.
    /// @param sender Sender address.
    /// @param recipient Recipient address.
    function rejectDelivery(
        address rvm_id,
        uint256 txh,
        uint256 index,
        uint256 amount,
        address sender,
        address recipient
    ) external authorizedSenderOnly rvmIdOnly(rvm_id) {
        MessageId memory message = MessageId({
            tx: txh,
            index: index,
            amount: amount,
            sender: sender,
            recipient: recipient
        });
        _processDeliveryRejection(message);
    }

    // Outbox transport

    /// @notice Initial message implemented as a log record intercepted by the Reactive Network.
    /// @inheritdoc AbstractBridgehead
    function _sendInitialMessage(MessageId memory id) override internal {
        emit InitialMessage(
            id.tx,
            id.index,
            id.amount,
            id.sender,
            id.recipient
        );
    }

    /// @notice Confirmation sending implemented as a log record intercepted by the Reactive Network.
    /// @inheritdoc AbstractBridgehead
    function _sendConfirmation(uint256 submsg_id, MessageId memory id) override internal {
        emit Confirmation(
            submsg_id,
            id.tx,
            id.index,
            id.amount,
            id.sender,
            id.recipient
        );
    }

    /// @notice Rejection sending implemented as a log record intercepted by the Reactive Network.
    /// @inheritdoc AbstractBridgehead
    function _sendRejection(uint256 submsg_id, MessageId memory id) override internal {
        emit Rejection(
            submsg_id,
            id.tx,
            id.index,
            id.amount,
            id.sender,
            id.recipient
        );
    }

    /// @notice Returning the message boils down to sending the locked tokens back to the sender.
    /// @inheritdoc AbstractBridgehead
    function _returnMessage(MessageId memory id) override internal {
        require(wreact.balanceOf(address(this)) >= id.amount, 'Insufficient funds');
        wreact.transfer(id.sender, id.amount);
    }

    /// @notice Finalizing the message is a no-op in token-locking mode. Burnable tokens are burned to finalize.
    /// @inheritdoc AbstractBridgehead
    function _finalizeMessage(MessageId memory id) override internal {
        require(wreact.balanceOf(address(this)) >= id.amount, 'Insufficient funds');
        if (is_mintable) {
            if (is_standard_burn) {
                wreact.burn(id.amount);
            } else {
                wreact.burn(address(this), id.amount);
            }
        }
    }

    // Inbox methods

    /// @notice Attempt to retry the delivery of a stuck message.
    /// @dev Must be attempted by the message recipient.
    /// @param uniqueish A reasonably unique-ish number identifying this transaction, provided by the client. Must be unique across messages with the same sender-recipient-amount combination.
    /// @param index `gasleft()` at the point where the original message ID has been computed.
    /// @param sender Sender's address on the Reactive Network side.
    /// @param recipient Recipient's address on the destination network side.
    /// @param amount Amount of ERC20 to be bridged. 1-to-1 minus the bridging fee.
    /// @param uniqueish_2 A reasonably unique-ish number to avoid confirmation request collision with the stuck one.
    function retry(uint256 uniqueish, uint256 index, address sender, address recipient, uint256 amount, uint256 uniqueish_2) external {
        MessageId memory message = MessageId({
            tx: uniqueish,
            index: index,
            amount: amount,
            sender: sender,
            recipient: recipient
        });
        _retry(_genId(uniqueish_2), message);
    }

    // Inbox callbacks

    /// @notice Entry point for initial messages received as callback transactions from the reactive part of the bridge.
    /// @param rvm_id RVM ID (i.e., reactive contract's deployer address) injected by the reactive node.
    /// @param txh Original unique-ish number identifying the message.
    /// @param index `gasleft()` at the moment the original message ID has been computed.
    /// @param amount Amount sent.
    /// @param sender Sender address.
    /// @param recipient Recipient address.
    function initialMessage(
        address rvm_id,
        uint256 txh,
        uint256 index,
        uint256 amount,
        address sender,
        address recipient
    ) external authorizedSenderOnly rvmIdOnly(rvm_id) {
        MessageId memory message = MessageId({
            tx: txh,
            index: index,
            amount: amount,
            sender: sender,
            recipient: recipient
        });
        _processInitialMessage(_genId(txh), message);
    }

    /// @notice Entry point for the message confirmations received as callback transactions from the reactive part of the bridge.
    /// @param rvm_id RVM ID (i.e., reactive contract's deployer address) injected by the reactive node.
    /// @param submsg_id ID of the confirmation request.
    /// @param txh Original unique-ish number identifying the message.
    /// @param index `gasleft()` at the moment the original message ID has been computed.
    /// @param amount Amount sent.
    /// @param sender Sender address.
    /// @param recipient Recipient address.
    function confirm(
        address rvm_id,
        uint256 submsg_id,
        uint256 txh,
        uint256 index,
        uint256 amount,
        address sender,
        address recipient
    ) external authorizedSenderOnly rvmIdOnly(rvm_id) {
        MessageId memory message = MessageId({
            tx: txh,
            index: index,
            amount: amount,
            sender: sender,
            recipient: recipient
        });
        _processConfirmation(submsg_id, _genId(submsg_id), message);
    }

    /// @notice Entry point for message rejections received as callback transactions from the reactive part of the bridge.
    /// @param rvm_id RVM ID (i.e., reactive contract's deployer address) injected by the reactive node.
    /// @param submsg_id ID of the confirmation request.
    /// @param txh Original unique-ish number identifying the message.
    /// @param index `gasleft()` at the moment the original message ID has been computed.
    /// @param amount Amount sent.
    /// @param sender Sender address.
    /// @param recipient Recipient address.
    function reject(
        address rvm_id,
        uint256 submsg_id,
        uint256 txh,
        uint256 index,
        uint256 amount,
        address sender,
        address recipient
    ) external authorizedSenderOnly rvmIdOnly(rvm_id) {
        MessageId memory message = MessageId({
            tx: txh,
            index: index,
            amount: amount,
            sender: sender,
            recipient: recipient
        });
        _processRejection(submsg_id, message);
    }

    // Inbox transport

    /// @notice Confirmation request sending implemented as a log record intercepted by the Reactive Network.
    /// @inheritdoc AbstractBridgehead
    function _sendConfirmationRequest(uint256 submsg_id, MessageId memory id) override internal {
        emit ConfirmationRequest(
            submsg_id,
            id.tx,
            id.index,
            id.amount,
            id.sender,
            id.recipient
        );
    }

    /// @notice Delivery is from token reserves if not mintable. New tokens are minted for delivery otherwise.
    /// @inheritdoc AbstractBridgehead
    function _deliver(MessageId memory id) override internal {
        if (is_mintable) {
            try wreact.mint(id.recipient, id.amount - _computeFee(id.amount)) {
                _confirmDelivery(id);
            } catch {
                _rejectDelivery(id);
            }
        } else {
            try wreact.transfer(id.recipient, id.amount - _computeFee(id.amount)) {
                _confirmDelivery(id);
            } catch {
                _rejectDelivery(id);
            }
        }
    }

    /// @notice Delivery confirmation sending implemented as a log record intercepted by the Reactive Network.
    /// @inheritdoc AbstractBridgehead
    function _sendDeliveryConfirmation(MessageId memory id) override internal {
        emit DeliveryConfirmation(
            id.tx,
            id.index,
            id.amount,
            id.sender,
            id.recipient
        );
    }

    /// @notice Delivery rejection sending implemented as a log record intercepted by the Reactive Network.
    /// @inheritdoc AbstractBridgehead
    function _sendDeliveryRejection(MessageId memory id) override internal {
        emit DeliveryRejection(
            id.tx,
            id.index,
            id.amount,
            id.sender,
            id.recipient
        );
    }
}
IERC20.sol 79 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}
AbstractCallback.sol 22 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.8.0;

import '../interfaces/IPayable.sol';
import './AbstractPayer.sol';

/// @title Abstract base contract for contracts receiving the Reactive Network callbacks.
abstract contract AbstractCallback is AbstractPayer {
    address internal rvm_id;

    constructor(address _callback_sender) {
        rvm_id = msg.sender;
        vendor = IPayable(payable(_callback_sender));
        addAuthorizedSender(_callback_sender);
    }

    modifier rvmIdOnly(address _rvm_id) {
        require(rvm_id == address(0) || rvm_id == _rvm_id, 'Authorized RVM ID only');
        _;
    }
}
AbstractDispenser.sol 52 lines
// SPDX-License-Identifier: GPL-2.0-or-later

pragma solidity >=0.8.0;

import '../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol';

/// @title Abstract base contract implementing ownership and withdrawal of stuck tokens.
contract AbstractDispenser {
    /// @notice Address of the contract's deployer/owner.
    address owner;

    /// @notice Indicates whether the contract is currently active.
    bool active;

    constructor() {
        owner = msg.sender;
        active = true;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, 'Not authorized');
        _;
    }

    modifier onlyActive() {
        require(active, 'Not active right now');
        _;
    }

    /// @notice Allows withdrawal of native or ERC20 tokens on the contract's balance.
    /// @param token Token address, or `0` for native.
    /// @param amount Amount to be withdrawn.
    function withdraw(address token, uint256 amount) external onlyOwner {
        if (token == address(0)) {
            require(address(this).balance >= amount, 'Insufficient funds');
            (bool success,) = payable(msg.sender).call{ value: amount }(new bytes(0));
            require(success, 'Failure');
        } else {
            require(IERC20(token).balanceOf(address(this)) >= amount, 'Insufficient funds');
            IERC20(token).transfer(msg.sender, amount);
        }
    }

    function pause() external onlyOwner onlyActive {
        active = false;
    }

    function unpause() external onlyOwner {
        require(!active, 'Already active');
        active = true;
    }
}
AbstractFeeCalculator.sol 29 lines
// SPDX-License-Identifier: GPL-2.0-or-later

pragma solidity >=0.8.0;

/// @title Abstract base contract implementing the calculation of bridge fees.
contract AbstractFeeCalculator {
    uint256 constant MAX_FIXED_FEE = 0.1 ether;
    uint256 constant MAX_PERC_FEE = 1000;

    /// @notice Fixed fee (in wei).
    uint256 fixed_fee;

    /// @notice Fee rate in 0.01%s.
    uint256 perc_fee;

    constructor(uint256 _fixed_fee, uint256 _perc_fee) {
        require(_fixed_fee <= MAX_FIXED_FEE, 'Fixed fee set too high');
        require(_perc_fee <= MAX_PERC_FEE, 'Fee rate set too high');
        fixed_fee = _fixed_fee;
        perc_fee = _perc_fee;
    }

    /// @notice Computed the total fee for the given amount to be bridged.
    /// @param amount Amount to be bridged in wei.
    /// @return Total fee to be withheld.
    function _computeFee(uint256 amount) internal view returns (uint256) {
        return fixed_fee + (amount * perc_fee / 10000);
    }
}
AbstractBridgehead.sol 279 lines
// SPDX-License-Identifier: GPL-2.0-or-later

pragma solidity >=0.8.0;

/// @title Abstract base contract implementing the abstract message transport protocol.
/// @dev Specific bridge implementation must implement concrete cross-chain transport.
abstract contract AbstractBridgehead {
    enum MessageStatus {
        NONE,
        REJECTED,
        PENDING,
        DELIVERING,
        DELIVERED
    }

    enum RequestStatus {
        NONE,
        SENT,
        RECEIVED
    }

    struct MessageId {
        uint256 tx;
        uint256 index;
        uint256 amount;
        address sender;
        address recipient;
    }

    struct MessageState {
        MessageStatus status;
        uint8 confirmations;
        uint256 last_touched;
        mapping (uint256 => RequestStatus) seen;
    }

    uint256 private constant INIT_SUBMSG_ID = 0;
    uint256 private constant REJECTED_SUBMSG_ID = 0xDEADBEEF;
    uint256 private constant DELIVERING_SUBMSG_ID = 0xCAFEBABE;
    uint256 private constant DELIVERED_SUBMSG_ID = 0xBEEFCAFE;

    /// @notice Number of active sender confirmation. required to fully confirm the delivery.
    uint8 confirmations;

    /// @notice Inddicates whether message cancellations are allowed.
    bool allow_cancellations;

    /// @notice Allowed cancellation threshold since last activity (in blocks).
    uint256 cancellation_threshold;

    /// @notice Maps outgoing message IDs to message states.
    mapping (uint256 => MessageState) outbox;

    /// @notice Maps incoming message IDs to message states.
    mapping (uint256 => MessageState) inbox;

    constructor(
        uint8 _confirmations,
        bool _allow_cancellations,
        uint256 _cancellation_threshold
    ) payable {
        confirmations = _confirmations;
        allow_cancellations = _allow_cancellations;
        cancellation_threshold = _cancellation_threshold;
    }

    // Outbox side methods

    /// @notice Implementing classes must implement delivery of the initial message.
    /// @param id Original message ID.
    function _sendInitialMessage(MessageId memory id) virtual internal;

    /// @notice Implementing classes must implement delivery of message confirmation.
    /// @param submsg_id Current message ID in the confirmation chain.
    /// @param id Original message ID.
    function _sendConfirmation(uint256 submsg_id, MessageId memory id) virtual internal;

    /// @notice Implementing classes must implement delivery of message rejection.
    /// @param submsg_id Current message ID in the confirmation chain.
    /// @param id Original message ID.
    function _sendRejection(uint256 submsg_id, MessageId memory id) virtual internal;

    /// @notice Implementing classes must implement returning of rejected messages.
    /// @param id Original message ID.
    function _returnMessage(MessageId memory id) virtual internal;

    /// @notice Implementing classes must implement returning of finalization of delivered messages (if required).
    /// @param id Original message ID.
    function _finalizeMessage(MessageId memory id) virtual internal;

    /// @notice Checks for duplicates, updates the message state, and triggers the delivery of the initial message.
    /// @param id Original message ID.
    function _sendMessage(MessageId memory id) internal {
        MessageState storage state = outbox[_hashId(id)];
        require(state.status == MessageStatus.NONE, '[SM] Duplicate message ID');
        _touch(state, INIT_SUBMSG_ID, MessageStatus.PENDING, RequestStatus.RECEIVED, false);
        _sendInitialMessage(id);
    }

    /// @notice Cancels the outgoing message if possible.
    /// @param id Original message ID.
    function _cancelMessage(MessageId memory id) internal {
        require(allow_cancellations, 'Cancellations not allowed');
        MessageState storage state = outbox[_hashId(id)];
        require(
            state.status == MessageStatus.PENDING && (block.number - state.last_touched) > cancellation_threshold,
            '[CM] Invalid message ID'
        );
        _touch(state, REJECTED_SUBMSG_ID, MessageStatus.REJECTED, RequestStatus.RECEIVED, false);
        _returnMessage(id);
    }

    /// @notice Verifies the incoming confirmation request, and sends confirmation or rejection as appropriate.
    /// @param submsg_id Current message ID in the confirmation chain.
    /// @param id Original message ID.
    function _processConfirmationRequest(uint256 submsg_id, MessageId memory id) internal {
        MessageState storage state = outbox[_hashId(id)];
        if (_valid(state, submsg_id, RequestStatus.NONE)) {
            _touch(state, submsg_id, MessageStatus.PENDING, RequestStatus.RECEIVED, true);
            _sendConfirmation(submsg_id, id);
        } else {
            _sendRejection(submsg_id, id);
        }
    }

    /// @notice Processes the delivery rejection of a given message, initiates message retun if verified..
    /// @param id Original message ID.
    function _processDeliveryRejection(MessageId memory id) internal {
        MessageState storage state = outbox[_hashId(id)];
        require(state.status == MessageStatus.PENDING, '[PDR] Invalid message ID');
        _touch(state, REJECTED_SUBMSG_ID, MessageStatus.REJECTED, RequestStatus.RECEIVED, false);
        _returnMessage(id);
    }

    /// @notice Processes the delivery confirmation of a given message, initiates message finalization if verified..
    /// @param id Original message ID.
    function _processDeliveryConfirmation(MessageId memory id) internal {
        MessageState storage state = outbox[_hashId(id)];
        require(state.status == MessageStatus.PENDING, '[PDC] Invalid message ID');
        _touch(state, DELIVERED_SUBMSG_ID, MessageStatus.DELIVERED, RequestStatus.RECEIVED, false);
        _finalizeMessage(id);
    }

    // Inbox side methods

    /// @notice Implementing classes must implement sending of confirmation requests.
    /// @param submsg_id Current message ID in the confirmation chain.
    /// @param id Original message ID.
    function _sendConfirmationRequest(uint256 submsg_id, MessageId memory id) virtual internal;

    /// @notice Implementing classes must implement the final message delivery.
    /// @param id Original message ID.
    function _deliver(MessageId memory id) virtual internal;

    /// @notice Implementing classes must implement the transport of delivery confirmations.
    /// @param id Original message ID.
    function _sendDeliveryConfirmation(MessageId memory id) virtual internal;

    /// @notice Implementing classes must implement the transport of delivery rejections.
    /// @param id Original message ID.
    function _sendDeliveryRejection(MessageId memory id) virtual internal;

    /// @notice Processes the initial message request, initiates the sending of initial confirmation requests.
    /// @param submsg_id Current message ID in the confirmation chain.
    /// @param id Original message ID.
    function _processInitialMessage(uint256 submsg_id, MessageId memory id) internal {
        MessageState storage state = inbox[_hashId(id)];
        require(state.status == MessageStatus.NONE, '[PIM] Duplicate message ID');
        _touch(state, INIT_SUBMSG_ID, MessageStatus.PENDING, RequestStatus.RECEIVED, false);
        _requestConfirmation(submsg_id, id);
    }

    /// @notice Allows to retry the delivery of stuck messages by requesting additional confirmations.
    /// @param submsg_id Current message ID in the confirmation chain.
    /// @param id Original message ID.
    function _retry(uint256 submsg_id, MessageId memory id) internal {
        MessageState storage state = inbox[_hashId(id)];
        require(state.status == MessageStatus.PENDING, '[R] Invalid message ID');
        _requestConfirmation(submsg_id, id);
    }

    /// @notice Requests the message confirmation from the outbox side of the bridge.
    /// @param submsg_id Current message ID in the confirmation chain.
    /// @param id Original message ID.
    function _requestConfirmation(uint256 submsg_id, MessageId memory id) internal {
        MessageState storage state = inbox[_hashId(id)];
        if (_valid(state, submsg_id, RequestStatus.NONE)) {
            _touch(state, submsg_id, MessageStatus.PENDING, RequestStatus.SENT, false);
            _sendConfirmationRequest(submsg_id, id);
        }
    }

    /// @notice Processed incoming message confirmation, requesting additional confirmation if needed, or delivering the message.
    /// @param submsg_id Current message ID in the confirmation chain.
    /// @param new_submsg_id Next message ID in the confirmation chain.
    /// @param id Original message ID.
    function _processConfirmation(uint256 submsg_id, uint256 new_submsg_id, MessageId memory id) internal {
        MessageState storage state = inbox[_hashId(id)];
        if (_valid(state, submsg_id, RequestStatus.SENT)) {
            _touch(state, submsg_id, MessageStatus.PENDING, RequestStatus.RECEIVED, true);
            if (state.confirmations >= confirmations) {
                _touch(state, DELIVERING_SUBMSG_ID, MessageStatus.DELIVERING, RequestStatus.RECEIVED, false);
                _deliver(id);
            } else {
                _requestConfirmation(new_submsg_id, id);
            }
        }
    }

    /// @notice Processes incoming message rejection.
    /// @param submsg_id Current message ID in the confirmation chain.
    /// @param id Original message ID.
    function _processRejection(uint256 submsg_id, MessageId memory id) internal {
        MessageState storage state = inbox[_hashId(id)];
        if (_valid(state, submsg_id, RequestStatus.SENT)) {
            _touch(state, DELIVERING_SUBMSG_ID, MessageStatus.DELIVERING, RequestStatus.RECEIVED, false);
            _rejectDelivery(id);
        }
    }

    /// @notice Triggers the transport of delivery confirmation to the outbox.
    /// @param id Original message ID.
    function _confirmDelivery(MessageId memory id) internal {
        MessageState storage state = inbox[_hashId(id)];
        require(state.status == MessageStatus.DELIVERING, '[CD] Invalid message ID');
        _touch(state, DELIVERED_SUBMSG_ID, MessageStatus.DELIVERED, RequestStatus.RECEIVED, false);
        _sendDeliveryConfirmation(id);
    }

    /// @notice Triggers the transport of delivery rejection to the outbox.
    /// @param id Original message ID.
    function _rejectDelivery(MessageId memory id) internal {
        MessageState storage state = inbox[_hashId(id)];
        require(state.status == MessageStatus.DELIVERING, '[RD] Invalid message ID');
        _touch(state, REJECTED_SUBMSG_ID, MessageStatus.REJECTED, RequestStatus.RECEIVED, false);
        _sendDeliveryRejection(id);
    }

    // Common methods

    /// @notice Update the message and submessage state.
    /// @param state Current message state.
    /// @param submsg_id Message ID of the current message in the confirmation chain.
    /// @param status New message status.
    /// @param rq_status New sub-message (confirmation request/confirmation) status.
    /// @param increase_confirmations Indicates whether the number of sent/received confirmations should be increased.
    function _touch(
        MessageState storage state,
        uint256 submsg_id,
        MessageStatus status,
        RequestStatus rq_status,
        bool increase_confirmations
    ) private {
        if (state.seen[submsg_id] != RequestStatus.RECEIVED) {
            state.seen[submsg_id] = rq_status;
            state.status = status;
            state.last_touched = block.number;
            if (increase_confirmations) {
                ++state.confirmations;
            }
        }
    }

    /// @notice Checks the validity of the current message state in accordance with provided parameters.
    /// @param state Message state to be checked.
    /// @param submsg_id Sub-message ID in the confirmation chain.
    /// @param status Expected sub-message status.
    /// @return Indicates whether the state matches expectations.
    function _valid(MessageState storage state, uint256 submsg_id, RequestStatus status) private view returns (bool) {
        return state.status == MessageStatus.PENDING && state.seen[submsg_id] == status;
    }

    /// @notice Computes the mapping key from the message.
    /// @param id Message to be processed.
    /// @return Mapping key for inbox/outbox.
    function _hashId(MessageId memory id) private pure returns (uint256) {
        return uint256(keccak256(abi.encode(id.tx, id.amount, id.sender, id.recipient)));
    }
}
BridgeLib.sol 10 lines
// SPDX-License-Identifier: GPL-2.0-or-later

pragma solidity >=0.8.0;

/// @notice Computes the next collision resistant key in message/confirmation chain from the previous one.
/// @param seed Original message ID or seed.
/// @return The next message id.
function _genId(uint256 seed) pure returns (uint256) {
    return uint256(keccak256(abi.encode(seed)));
}
IPayable.sol 14 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.8.0;

/// @title Common interface for the system contract and the callback proxy, allows contracts to check and pay their debts.
interface IPayable {
    /// @notice Allows contracts to pay their debts and resume subscriptions.
    receive() external payable;

    /// @notice Allows reactive contracts to check their outstanding debt.
    /// @param _contract Reactive contract's address.
    /// @return Reactive contract's current debt due to unpaid reactive transactions and/or callbacks.
    function debt(address _contract) external view returns (uint256);
}
AbstractPayer.sol 60 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.8.0;

import '../interfaces/IPayer.sol';
import '../interfaces/IPayable.sol';

/// @title Abstract base contract for contracts needing to handle payments to the system contract or callback proxies.
abstract contract AbstractPayer is IPayer {
    IPayable internal vendor;

    /// @notice ACL for addresses allowed to make callbacks and/or request payment.
    mapping(address => bool) senders;

    constructor() {
    }

    /// @inheritdoc IPayer
    receive() virtual external payable {
    }

    modifier authorizedSenderOnly() {
        require(senders[msg.sender], 'Authorized sender only');
        _;
    }

    /// @inheritdoc IPayer
    function pay(uint256 amount) external authorizedSenderOnly {
        _pay(payable(msg.sender), amount);
    }

    /// @notice Automatically cover the outstanding debt to the system contract or callback proxy, provided the contract has sufficient funds.
    function coverDebt() external {
        uint256 amount = vendor.debt(address(this));
        _pay(payable(vendor), amount);
    }

    /// @notice Attempts to safely transfer the specified sum to the given address.
    /// @param recipient Address of the transfer's recipient.
    /// @param amount Amount to be transferred.
    function _pay(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, 'Insufficient funds');
        if (amount > 0) {
            (bool success,) = payable(recipient).call{value: amount}(new bytes(0));
            require(success, 'Transfer failed');
        }
    }

    /// @notice Adds the given address to the ACL.
    /// @param sender Sender address to add.
    function addAuthorizedSender(address sender) internal {
        senders[sender] = true;
    }

    /// @notice Removes the given address from the ACL.
    /// @param sender Sender address to remove.
    function removeAuthorizedSender(address sender) internal {
        senders[sender] = false;
    }
}
IPayer.sol 14 lines
// SPDX-License-Identifier: UNLICENSED

pragma solidity >=0.8.0;

/// @title Common interface for the contracts that need to pay for system contract's or proxies' services.
interface IPayer {
    /// @notice Method called by the system contract and/or proxies when payment is due.
    /// @dev Make sure to check the msg.sender.
    /// @param amount Amount owed due to reactive transactions and/or callbacks.
    function pay(uint256 amount) external;

    /// @notice Allows the reactive contracts and callback contracts to receive funds for their operational expenses.
    receive() external payable;
}

Read Contract

gas_fee 0x76369b6d → uint256

Write Contract 14 functions

These functions modify contract state and require a wallet transaction to execute.

bridge 0x2f820a5f
uint256 uniqueish
address recipient
uint256 amount
cancel 0x7c960ff4
uint256 uniqueish
uint256 index
address recipient
uint256 amount
confirm 0x6f48595a
address rvm_id
uint256 submsg_id
uint256 txh
uint256 index
uint256 amount
address sender
address recipient
confirmDelivery 0x6944af66
address rvm_id
uint256 txh
uint256 index
uint256 amount
address sender
address recipient
coverDebt 0x7a90b990
No parameters
initialMessage 0xa45c265a
address rvm_id
uint256 txh
uint256 index
uint256 amount
address sender
address recipient
pause 0x8456cb59
No parameters
pay 0xc290d691
uint256 amount
reject 0x035a2875
address rvm_id
uint256 submsg_id
uint256 txh
uint256 index
uint256 amount
address sender
address recipient
rejectDelivery 0x668cec3b
address rvm_id
uint256 txh
uint256 index
uint256 amount
address sender
address recipient
requestConfirmation 0x2f14b9e6
address rvm_id
uint256 submsg_id
uint256 txh
uint256 index
uint256 amount
address sender
address recipient
retry 0xe52eb1e3
uint256 uniqueish
uint256 index
address sender
address recipient
uint256 amount
uint256 uniqueish_2
unpause 0x3f4ba83a
No parameters
withdraw 0xf3fef3a3
address token
uint256 amount

Recent Transactions

Transaction index is loading. Only unfinalized transactions are shown while the index starts up.