diff --git a/contracts/Token.sol b/contracts/Token.sol index 76261a9..c410a68 100644 --- a/contracts/Token.sol +++ b/contracts/Token.sol @@ -1,10 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; -contract Nickle is ERC20 { - constructor() ERC20("Nickle", "NICKLE") { +contract Nickle is ERC20Upgradeable { + function initialize() public initializer { + __ERC20_init("Nickle", "NICKLE"); _mint( msg.sender, 10000000000000000000000000000000000000000000000000000000000000000 @@ -12,8 +13,9 @@ contract Nickle is ERC20 { } } -contract BronzeCowry is ERC20 { - constructor() ERC20("Bronze Cowry", "SHELL") { +contract BronzeCowry is ERC20Upgradeable { + function initialize() public initializer { + __ERC20_init("Bronze Cowry", "SHELL"); _mint( msg.sender, 10000000000000000000000000000000000000000000000000000000000000000 @@ -21,8 +23,9 @@ contract BronzeCowry is ERC20 { } } -contract AthenianDrachma is ERC20 { - constructor() ERC20("Athenian Drachma", "ATH") { +contract AthenianDrachma is ERC20Upgradeable { + function initialize() public initializer { + __ERC20_init("Athenian Drachma", "ATH"); _mint( msg.sender, 10000000000000000000000000000000000000000000000000000000000000000 @@ -30,8 +33,9 @@ contract AthenianDrachma is ERC20 { } } -contract DebasedTowerPoundSterling is ERC20 { - constructor() ERC20("DebasedTowerPoundSterling", "NEWTON") { +contract DebasedTowerPoundSterling is ERC20Upgradeable { + function initialize() public initializer { + __ERC20_init("DebasedTowerPoundSterling", "NEWTON"); _mint( msg.sender, 10000000000000000000000000000000000000000000000000000000000000000 diff --git a/contracts/axelar/AxelarExecutable.sol b/contracts/axelar/AxelarExecutable.sol new file mode 100644 index 0000000..417d48d --- /dev/null +++ b/contracts/axelar/AxelarExecutable.sol @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {IAxelarGateway} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol"; +import {IAxelarExecutable} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarExecutable.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +/** + * @title AxelarExecutable + * @dev Abstract contract to be inherited by contracts that need to execute cross-chain commands via Axelar's Gateway. + * It implements the IAxelarExecutable interface and is upgradeable. + */ +abstract contract AxelarExecutable is IAxelarExecutable, Initializable { + /// @dev Reference to the Axelar Gateway contract. + address internal gatewayAddress; + + /** + * @dev Contract constructor that disables initializers. + */ + constructor() { + _disableInitializers(); + } + + /** + * @dev Initializes the contract with the Axelar Gateway address. + * Reverts if the provided address is the zero address. + * @param gateway_ The address of the Axelar Gateway contract. + */ + function __AxelarExecutable_init( + address gateway_ + ) internal onlyInitializing { + if (gateway_ == address(0)) revert InvalidAddress(); + gatewayAddress = gateway_; + } + + /** + * @notice Executes the cross-chain command after validating it with the Axelar Gateway. + * @dev This function ensures the call is approved by Axelar Gateway before execution. + * It uses a hash of the payload for validation and internally calls _execute for the actual command execution. + * Reverts if the validation fails. + * @param commandId The unique identifier of the cross-chain message being executed. + * @param sourceChain The name of the source chain from which the message originated. + * @param sourceAddress The address on the source chain that sent the message. + * @param payload The payload of the message payload. + */ + function execute( + bytes32 commandId, + string calldata sourceChain, + string calldata sourceAddress, + bytes calldata payload + ) external virtual { + bytes32 payloadHash = keccak256(payload); + + if ( + !gateway().validateContractCall( + commandId, + sourceChain, + sourceAddress, + payloadHash + ) + ) revert NotApprovedByGateway(); + + _execute(commandId, sourceChain, sourceAddress, payload); + } + + /** + * @dev Internal virtual function to be overridden by child contracts to execute the command. + * It allows child contracts to define their custom command execution logic. + * @param commandId The identifier of the command to execute. + * @param sourceChain The name of the source chain from which the command originated. + * @param sourceAddress The address on the source chain that sent the command. + * @param payload The payload of the command to be executed. + */ + function _execute( + bytes32 commandId, + string calldata sourceChain, + string calldata sourceAddress, + bytes calldata payload + ) internal virtual; + + /** + * @notice Returns the address of the AxelarGateway contract. + * @return The Axelar Gateway instance. + */ + function gateway() public view returns (IAxelarGateway) { + return IAxelarGateway(gatewayAddress); + } +} diff --git a/contracts/destChain/Prover-Axelar.sol b/contracts/destChain/Prover-Axelar.sol index c7e460b..8a63cee 100644 --- a/contracts/destChain/Prover-Axelar.sol +++ b/contracts/destChain/Prover-Axelar.sol @@ -15,7 +15,7 @@ import {Misc} from "filecoin-solidity-api/contracts/v0.8/utils/Misc.sol"; import {FilAddresses} from "filecoin-solidity-api/contracts/v0.8/utils/FilAddresses.sol"; import {DataAttestation, IBridgeContract, StringsEqual} from "../sourceChain/Oracles.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; -import {AxelarExecutable} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol"; +import {AxelarExecutable} from "../axelar/AxelarExecutable.sol"; import {IAxelarGateway} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol"; import {IAxelarGasService} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol"; @@ -25,7 +25,7 @@ contract DealClientAxl is AxelarExecutable { using AccountCBOR for *; using MarketCBOR for *; - IAxelarGasService public immutable gasService; + IAxelarGasService public gasService; uint64 public constant AUTHENTICATE_MESSAGE_METHOD_NUM = 2643134072; uint64 public constant DATACAP_RECEIVER_HOOK_METHOD_NUM = 3726118371; uint64 public constant MARKET_NOTIFY_DEAL_METHOD_NUM = 4186741094; @@ -94,19 +94,22 @@ contract DealClientAxl is AxelarExecutable { uint256 price ); event ReceivedDataCap(string received); - event DealNotify( - uint64 dealId, - bytes commP, - bytes chainId - ); + event DealNotify(uint64 dealId, bytes commP, bytes chainId); event XChainProveDataStored( - string destinationChain, - string destinationAddress); + string destinationChain, + string destinationAddress + ); - constructor( - address _gateway, - address _gasReceiver - ) AxelarExecutable(_gateway) { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize( + address _gateway, + address _gasReceiver + ) public initializer { + __AxelarExecutable_init(_gateway); gasService = IAxelarGasService(_gasReceiver); } @@ -134,9 +137,14 @@ contract DealClientAxl is AxelarExecutable { } } - function getSourceChain(uint256 chainId) public view returns (string memory, address) { + function getSourceChain( + uint256 chainId + ) public view returns (string memory, address) { SourceChain memory source = chainIdToSourceChain[chainId]; - require(source.sourceOracleAddress != address(0), "chainId Chain is not configured in Prover Contract"); + require( + source.sourceOracleAddress != address(0), + "chainId Chain is not configured in Prover Contract" + ); return (source.chainName, source.sourceOracleAddress); } @@ -144,32 +152,45 @@ contract DealClientAxl is AxelarExecutable { providerGasFunds[providerAddrData] += msg.value; } - function makeDealProposal(DealRequest calldata deal) public returns (bytes32){ + function makeDealProposal( + DealRequest calldata deal + ) public returns (bytes32) { require( - pieceStatus[deal.piece_cid] != Status.DealActivated || - pieceStatus[deal.piece_cid] != Status.DealPublished, + pieceStatus[deal.piece_cid] != Status.DealActivated || + pieceStatus[deal.piece_cid] != Status.DealPublished, "this deal is already active or published" ); uint256 idx = dealRequests.length; dealRequests.push(deal); - bytes32 proposalId = keccak256(abi.encodePacked(msg.sender, block.timestamp, idx)); + bytes32 proposalId = keccak256( + abi.encodePacked(msg.sender, block.timestamp, idx) + ); dealIdToIndex[proposalId] = RequestIdx(idx, true); pieceRequests[deal.piece_cid] = RequestId(proposalId, true); pieceStatus[deal.piece_cid] = Status.DealPublished; - emit DealProposalCreate(proposalId, deal.piece_size, deal.verified_deal, deal.storage_price_per_epoch); + emit DealProposalCreate( + proposalId, + deal.piece_size, + deal.verified_deal, + deal.storage_price_per_epoch + ); return proposalId; } - function getDealRequest(bytes32 proposalId) public view returns (DealRequest memory) { + function getDealRequest( + bytes32 proposalId + ) public view returns (DealRequest memory) { require(dealIdToIndex[proposalId].valid, "Deal does not exist"); return dealRequests[dealIdToIndex[proposalId].idx]; } - function getDealProposal(bytes32 proposalId) public view returns (bytes memory) { + function getDealProposal( + bytes32 proposalId + ) public view returns (bytes memory) { DealRequest memory deal = getDealRequest(proposalId); MarketTypes.DealProposal memory proposal = MarketTypes.DealProposal( CommonTypes.Cid(deal.piece_cid), @@ -188,7 +209,9 @@ contract DealClientAxl is AxelarExecutable { return MarketCBOR.serializeDealProposal(proposal); } - function serializeExtraParams(ExtraParams memory params) internal pure returns (bytes memory) { + function serializeExtraParams( + ExtraParams memory params + ) internal pure returns (bytes memory) { CBOR.CBORBuffer memory buffer = CBOR.create(64); buffer.startFixedArray(4); buffer.writeString(params.location_ref); @@ -199,20 +222,26 @@ contract DealClientAxl is AxelarExecutable { return buffer.data(); } - function getExtraParams(bytes32 proposalId) public view returns (bytes memory extra_params) { + function getExtraParams( + bytes32 proposalId + ) public view returns (bytes memory extra_params) { DealRequest memory deal = getDealRequest(proposalId); return serializeExtraParams(deal.extra_params); } function updateDealStatus(bytes memory pieceCid) public { - require(pieceDeals[pieceCid] > 0, "Deal does not exist for piece cid"); - (int256 exit_code, MarketTypes.GetDealActivationReturn memory ret) = MarketAPI - .getDealActivation(pieceDeals[pieceCid]); - - require(exit_code == 0, "Deal activation failed with non zero exit code"); - + ( + int256 exit_code, + MarketTypes.GetDealActivationReturn memory ret + ) = MarketAPI.getDealActivation(pieceDeals[pieceCid]); + + require( + exit_code == 0, + "Deal activation failed with non zero exit code" + ); + if (CommonTypes.ChainEpoch.unwrap(ret.terminated) > 0) { pieceStatus[pieceCid] = Status.DealTerminated; } else if (CommonTypes.ChainEpoch.unwrap(ret.activated) > 0) { @@ -251,15 +280,15 @@ contract DealClientAxl is AxelarExecutable { ); bytes memory payload = abi.encode(attest); - emit DealNotify(mdnp.dealId, + emit DealNotify( + mdnp.dealId, proposal.piece_cid.data, proposal.label.data ); if (chainId == block.chainid) { - IBridgeContract( - chainIdToSourceChain[chainId].sourceOracleAddress - )._execute( + IBridgeContract(chainIdToSourceChain[chainId].sourceOracleAddress) + ._execute( chainIdToSourceChain[chainId].chainName, addressToHexString(address(this)), payload @@ -290,8 +319,7 @@ contract DealClientAxl is AxelarExecutable { // gasFunds = providerGasFunds[providerAddrData]; // providerGasFunds[providerAddrData] = 0; // } - string memory sourceChain = chainIdToSourceChain[chainId] - .chainName; + string memory sourceChain = chainIdToSourceChain[chainId].chainName; string memory sourceOracleAddress = addressToHexString( chainIdToSourceChain[chainId].sourceOracleAddress ); @@ -310,8 +338,9 @@ contract DealClientAxl is AxelarExecutable { bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, - bytes calldata payload) internal override{ - //Do Nothing + bytes calldata payload + ) internal override { + //Do Nothing } function debug_call( @@ -328,9 +357,8 @@ contract DealClientAxl is AxelarExecutable { ); bytes memory payload = abi.encode(attest); if (chainId == block.chainid) { - IBridgeContract( - chainIdToSourceChain[chainId].sourceOracleAddress - )._execute( + IBridgeContract(chainIdToSourceChain[chainId].sourceOracleAddress) + ._execute( chainIdToSourceChain[chainId].chainName, addressToHexString(address(this)), payload diff --git a/contracts/destChain/Prover.sol b/contracts/destChain/Prover.sol index 5c4ab07..d7e9a4c 100644 --- a/contracts/destChain/Prover.sol +++ b/contracts/destChain/Prover.sol @@ -15,10 +15,11 @@ import {Misc} from "filecoin-solidity-api/contracts/v0.8/utils/Misc.sol"; import {FilAddresses} from "filecoin-solidity-api/contracts/v0.8/utils/FilAddresses.sol"; import {DataAttestation, IBridgeContract, StringsEqual} from "../sourceChain/Oracles.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; using CBOR for CBOR.CBORBuffer; -contract DealClient { +contract DealClient is Initializable { using AccountCBOR for *; using MarketCBOR for *; @@ -42,6 +43,12 @@ contract DealClient { IBridgeContract public bridgeContract; + constructor() { + _disableInitializers(); + } + + function initialize() public initializer {} + function setBridgeContract(address _bridgeContract) external { if (address(bridgeContract) == address(0)) { bridgeContract = IBridgeContract(_bridgeContract); diff --git a/contracts/sourceChain/OnRamp.sol b/contracts/sourceChain/OnRamp.sol index 880f7ff..5af5a39 100644 --- a/contracts/sourceChain/OnRamp.sol +++ b/contracts/sourceChain/OnRamp.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.17; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {Cid} from "../Cid.sol"; import {TRUNCATOR} from "../Const.sol"; @@ -73,7 +74,7 @@ contract PODSIVerifier { } } -contract OnRampContract is PODSIVerifier { +contract OnRampContract is PODSIVerifier, Initializable { enum OfferStatus { Pending, Aggregated, @@ -94,14 +95,15 @@ contract OnRampContract is PODSIVerifier { event DataReady(Offer offer, uint64 id); event AggregationCommitted( - uint64 aggId, + uint64 aggId, bytes commP, - uint64[] offerIDs, - address payoutAddr); + uint64[] offerIDs, + address payoutAddr + ); event ProveDataStored(bytes commP, uint64 dealID); - uint64 private nextOfferId = 1; - uint64 private nextAggregateID = 1; + uint64 private nextOfferId; + uint64 private nextAggregateID; address public dataProofOracle; mapping(uint64 => Offer) public offers; mapping(uint64 => uint64[]) public aggregations; @@ -113,6 +115,15 @@ contract OnRampContract is PODSIVerifier { mapping(uint64 => uint64) private offerToAggregationId; mapping(uint64 => uint64) public aggregationDealIds; + constructor() { + _disableInitializers(); + } + + function initialize() public initializer { + nextOfferId = 1; + nextAggregateID = 1; + } + function setOracle(address oracle_) external { if (dataProofOracle == address(0)) { dataProofOracle = oracle_; @@ -139,7 +150,7 @@ contract OnRampContract is PODSIVerifier { token: offer.token, status: OfferStatus.Pending }); - + offers[id] = newOffer; clientOffers[msg.sender].push(id); @@ -147,21 +158,23 @@ contract OnRampContract is PODSIVerifier { return id; } - function getClientOffers(address client) external view returns (uint64[] memory) { + function getClientOffers( + address client + ) external view returns (uint64[] memory) { return clientOffers[client]; } function getPendingOffers() external view returns (uint64[] memory) { uint64[] memory pending = new uint64[](nextOfferId - 1); uint64 count = 0; - + for (uint64 i = 1; i < nextOfferId; i++) { if (!isOfferAggregated[i]) { pending[count] = i; count++; } } - + assembly { mstore(pending, count) } @@ -172,18 +185,24 @@ contract OnRampContract is PODSIVerifier { return nextOfferId - 1; } - function getOfferDetails(uint64 offerId) external view returns ( - bytes memory commP, - uint64 size, - string memory location, - uint256 amount, - IERC20 token, - bool exists, - OfferStatus status - ) { + function getOfferDetails( + uint64 offerId + ) + external + view + returns ( + bytes memory commP, + uint64 size, + string memory location, + uint256 amount, + IERC20 token, + bool exists, + OfferStatus status + ) + { Offer memory offer = offers[offerId]; exists = offer.size != 0; - + if (exists) { return ( offer.commP, @@ -197,13 +216,12 @@ contract OnRampContract is PODSIVerifier { } } - function getOfferStatus(uint64 offerId) external view returns ( - bool exists, - OfferStatus status - ) { + function getOfferStatus( + uint64 offerId + ) external view returns (bool exists, OfferStatus status) { Offer memory offer = offers[offerId]; exists = offer.size != 0; - + if (exists) { status = offer.status; } @@ -231,7 +249,7 @@ contract OnRampContract is PODSIVerifier { ); isOfferAggregated[offerID] = true; offerToAggregationId[offerID] = aggId; - + offers[offerID].status = OfferStatus.Aggregated; } aggregations[aggId] = offerIDs; @@ -240,17 +258,21 @@ contract OnRampContract is PODSIVerifier { emit AggregationCommitted(aggId, commP, offerIDs, payoutAddr); } - function getAggregationDetails(uint64 aggId) external view returns ( - address payoutAddress, - bool isProven, - uint64 offerCount - ) { + function getAggregationDetails( + uint64 aggId + ) + external + view + returns (address payoutAddress, bool isProven, uint64 offerCount) + { payoutAddress = aggregationPayout[aggId]; isProven = provenAggregations[aggId]; offerCount = uint64(aggregations[aggId].length); } - function getAggregationOffers(uint64 aggId) external view returns (uint64[] memory) { + function getAggregationOffers( + uint64 aggId + ) external view returns (uint64[] memory) { return aggregations[aggId]; } @@ -283,21 +305,25 @@ contract OnRampContract is PODSIVerifier { //transfer payment to the receiver if the payment amount > 0 for (uint i = 0; i < aggregations[aggID].length; i++) { uint64 offerID = aggregations[aggID][i]; - + offers[offerID].status = OfferStatus.Proven; - - if(offers[offerID].amount > 0){ - require(offers[offerID].token.transfer( - aggregationPayout[aggID], - offers[offerID].amount), - "Payment transfer failed" + + if (offers[offerID].amount > 0) { + require( + offers[offerID].token.transfer( + aggregationPayout[aggID], + offers[offerID].amount + ), + "Payment transfer failed" ); } } provenAggregations[aggID] = true; } - function getOfferDealId(uint64 offerId) external view returns (uint64 dealId, bool exists) { + function getOfferDealId( + uint64 offerId + ) external view returns (uint64 dealId, bool exists) { if (isOfferAggregated[offerId]) { uint64 aggId = offerToAggregationId[offerId]; dealId = aggregationDealIds[aggId]; diff --git a/contracts/sourceChain/Oracles.sol b/contracts/sourceChain/Oracles.sol index c3e27cd..f480568 100644 --- a/contracts/sourceChain/Oracles.sol +++ b/contracts/sourceChain/Oracles.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.17; -import "@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol"; +import "../axelar/AxelarExecutable.sol"; import "@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/AddressString.sol"; interface IBridgeContract { @@ -36,7 +36,14 @@ contract AxelarBridge is AxelarExecutable { ); using StringToAddress for string; - constructor(address _gateway) AxelarExecutable(_gateway) {} + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize(address _gateway) public initializer { + __AxelarExecutable_init(_gateway); + } function setSenderReceiver(address sender_, address receiver_) external { receiver = receiver_; @@ -48,7 +55,7 @@ contract AxelarBridge is AxelarExecutable { string calldata _sourceChain_, string calldata sourceAddress_, bytes calldata payload_ - ) internal override{ + ) internal override { require(!executedCommands[commandId], "Command already executed"); DataAttestation memory attestation = abi.decode( payload_, @@ -137,14 +144,23 @@ contract DebugMockBridge is IBridgeContract { contract AxelarBridgeDebug is AxelarExecutable { event ReceivedAttestation(bytes commP, string sourceAddress); //tracks whether a command has already been executed using the executedCommands mapping. - mapping(bytes32 => bool) public executedCommands; - constructor(address _gateway) AxelarExecutable(_gateway) {} + mapping(bytes32 => bool) public executedCommands; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize(address _gateway) public initializer { + __AxelarExecutable_init(_gateway); + } + function _execute( bytes32 commandId, string calldata, string calldata sourceAddress_, bytes calldata payload_ - ) internal override{ + ) internal override { //Prevents replay attacks by ensuring that a command cannot be executed more than once. require(!executedCommands[commandId], "Command already executed"); executedCommands[commandId] = true; diff --git a/hardhat.config.ts b/hardhat.config.ts index 09a911f..a48c51c 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -97,7 +97,7 @@ if (env === "mainnet") { */ const config: HardhatUserConfig = { solidity: { - version: "0.8.21", + version: "0.8.22", settings: { optimizer: { enabled: true, diff --git a/package-lock.json b/package-lock.json index 3d5fea7..ca53bfc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "dependencies": { "@axelar-network/axelar-chains-config": "^1.3.0", + "@openzeppelin/contracts-upgradeable": "^5.3.0", "dotenv": "^16.0.3", "ts-node": "^10.9.2" }, @@ -2234,10 +2235,19 @@ "dev": true }, "node_modules/@openzeppelin/contracts": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.1.0.tgz", - "integrity": "sha512-p1ULhl7BXzjjbha5aqst+QMLY+4/LCWADXOCsmLHRM77AqiPjnd9vvUN9sosUfhL9JGKpZ0TjEGxgvnizmWGSA==", - "dev": true + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.3.0.tgz", + "integrity": "sha512-zj/KGoW7zxWUE8qOI++rUM18v+VeLTTzKs/DJFkSzHpQFPD/jKKF0TrMxBfGLl3kpdELCNccvB3zmofSzm4nlA==", + "license": "MIT" + }, + "node_modules/@openzeppelin/contracts-upgradeable": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-5.3.0.tgz", + "integrity": "sha512-yVzSSyTMWO6rapGI5tuqkcLpcGGXA0UA1vScyV5EhE5yw8By3Ewex9rDUw8lfVw0iTkvR/egjfcW5vpk03lqZg==", + "license": "MIT", + "peerDependencies": { + "@openzeppelin/contracts": "5.3.0" + } }, "node_modules/@scure/base": { "version": "1.1.9", diff --git a/package.json b/package.json index 8be2476..d21c361 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ }, "dependencies": { "@axelar-network/axelar-chains-config": "^1.3.0", + "@openzeppelin/contracts-upgradeable": "^5.3.0", "dotenv": "^16.0.3", "ts-node": "^10.9.2" }