mirror of
https://github.com/datahaven-xyz/datahaven
synced 2026-05-24 09:50:01 +00:00
Add era replay guard for rewards submissions
This commit is contained in:
parent
e60363ecc3
commit
d857ba7d2b
6 changed files with 168 additions and 26 deletions
|
|
@ -83,9 +83,12 @@ contract DataHavenServiceManager is OwnableUpgradeable, IAVSRegistrar, IDataHave
|
|||
/// `contracts/deployments/<chain>.json`.
|
||||
string private _version;
|
||||
|
||||
/// @notice Tracks whether rewards have already been submitted for a source-chain era and token.
|
||||
mapping(uint32 => mapping(address => bool)) public rewardsSubmittedForEra;
|
||||
|
||||
/// @notice Storage gap for upgradeability (must be at end of state variables)
|
||||
// solhint-disable-next-line var-name-mixedcase
|
||||
uint256[42] private __GAP;
|
||||
uint256[41] private __GAP;
|
||||
|
||||
// ============ Modifiers ============
|
||||
|
||||
|
|
@ -521,6 +524,7 @@ contract DataHavenServiceManager is OwnableUpgradeable, IAVSRegistrar, IDataHave
|
|||
|
||||
/// @inheritdoc IDataHavenServiceManager
|
||||
function submitRewards(
|
||||
uint32 eraIndex,
|
||||
IRewardsCoordinatorTypes.OperatorDirectedRewardsSubmission calldata submission
|
||||
) external override onlyRewardsInitiator {
|
||||
IRewardsCoordinatorTypes.OperatorDirectedRewardsSubmission memory translatedSubmission =
|
||||
|
|
@ -554,6 +558,12 @@ contract DataHavenServiceManager is OwnableUpgradeable, IAVSRegistrar, IDataHave
|
|||
|
||||
_sortOperatorRewards(translatedSubmission.operatorRewards);
|
||||
|
||||
address token = address(submission.token);
|
||||
require(
|
||||
!rewardsSubmittedForEra[eraIndex][token], RewardsAlreadySubmittedForEra(eraIndex, token)
|
||||
);
|
||||
rewardsSubmittedForEra[eraIndex][token] = true;
|
||||
|
||||
submission.token.safeIncreaseAllowance(address(_REWARDS_COORDINATOR), totalAmount);
|
||||
|
||||
IRewardsCoordinatorTypes.OperatorDirectedRewardsSubmission[] memory submissions =
|
||||
|
|
|
|||
|
|
@ -57,6 +57,9 @@ interface IDataHavenServiceManagerErrors {
|
|||
|
||||
/// @notice Thrown when the caller is not the ProxyAdmin
|
||||
error NotProxyAdmin();
|
||||
|
||||
/// @notice Thrown when rewards for an era and token have already been submitted
|
||||
error RewardsAlreadySubmittedForEra(uint32 eraIndex, address token);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -186,6 +189,16 @@ interface IDataHavenServiceManager is
|
|||
address solochainAddress
|
||||
) external view returns (address);
|
||||
|
||||
/**
|
||||
* @notice Returns whether rewards have already been submitted for an era and token
|
||||
* @param eraIndex The source-chain era index for the rewards submission
|
||||
* @param token The reward token address
|
||||
*/
|
||||
function rewardsSubmittedForEra(
|
||||
uint32 eraIndex,
|
||||
address token
|
||||
) external view returns (bool);
|
||||
|
||||
/**
|
||||
* @notice Initializes the DataHaven Service Manager
|
||||
* @param initialOwner Address of the initial owner (AVS owner)
|
||||
|
|
@ -332,13 +345,16 @@ interface IDataHavenServiceManager is
|
|||
|
||||
/**
|
||||
* @notice Submit rewards to EigenLayer
|
||||
* @param eraIndex The source-chain era index associated with the submission
|
||||
* @param submission The operator-directed rewards submission containing all reward parameters
|
||||
* @dev Only callable by the authorized Snowbridge Agent
|
||||
* @dev Strategies must be sorted in ascending order by address
|
||||
* @dev Operators must be sorted in ascending order by address
|
||||
* @dev Token must be pre-approved or held by the ServiceManager
|
||||
* @dev Only one submission is allowed per era and reward token
|
||||
*/
|
||||
function submitRewards(
|
||||
uint32 eraIndex,
|
||||
IRewardsCoordinatorTypes.OperatorDirectedRewardsSubmission calldata submission
|
||||
) external;
|
||||
|
||||
|
|
|
|||
|
|
@ -105,12 +105,20 @@
|
|||
"type": "t_string_storage"
|
||||
},
|
||||
{
|
||||
"astId": 23922,
|
||||
"astId": 23924,
|
||||
"contract": "src/DataHavenServiceManager.sol:DataHavenServiceManager",
|
||||
"label": "rewardsSubmittedForEra",
|
||||
"offset": 0,
|
||||
"slot": "109",
|
||||
"type": "t_mapping(t_uint32,t_mapping(t_address,t_bool))"
|
||||
},
|
||||
{
|
||||
"astId": 23929,
|
||||
"contract": "src/DataHavenServiceManager.sol:DataHavenServiceManager",
|
||||
"label": "__GAP",
|
||||
"offset": 0,
|
||||
"slot": "109",
|
||||
"type": "t_array(t_uint256)42_storage"
|
||||
"slot": "110",
|
||||
"type": "t_array(t_uint256)41_storage"
|
||||
}
|
||||
],
|
||||
"types": {
|
||||
|
|
@ -119,10 +127,10 @@
|
|||
"label": "address",
|
||||
"numberOfBytes": "20"
|
||||
},
|
||||
"t_array(t_uint256)42_storage": {
|
||||
"t_array(t_uint256)41_storage": {
|
||||
"encoding": "inplace",
|
||||
"label": "uint256[42]",
|
||||
"numberOfBytes": "1344",
|
||||
"label": "uint256[41]",
|
||||
"numberOfBytes": "1312",
|
||||
"base": "t_uint256"
|
||||
},
|
||||
"t_array(t_uint256)49_storage": {
|
||||
|
|
@ -173,6 +181,13 @@
|
|||
"numberOfBytes": "32",
|
||||
"value": "t_uint96"
|
||||
},
|
||||
"t_mapping(t_uint32,t_mapping(t_address,t_bool))": {
|
||||
"encoding": "mapping",
|
||||
"key": "t_uint32",
|
||||
"label": "mapping(uint32 => mapping(address => bool))",
|
||||
"numberOfBytes": "32",
|
||||
"value": "t_mapping(t_address,t_bool)"
|
||||
},
|
||||
"t_string_storage": {
|
||||
"encoding": "bytes",
|
||||
"label": "string",
|
||||
|
|
@ -183,6 +198,11 @@
|
|||
"label": "uint256",
|
||||
"numberOfBytes": "32"
|
||||
},
|
||||
"t_uint32": {
|
||||
"encoding": "inplace",
|
||||
"label": "uint32",
|
||||
"numberOfBytes": "4"
|
||||
},
|
||||
"t_uint8": {
|
||||
"encoding": "inplace",
|
||||
"label": "uint8",
|
||||
|
|
|
|||
|
|
@ -98,6 +98,12 @@ contract RewardsSubmitterTest is AVSDeployer {
|
|||
});
|
||||
}
|
||||
|
||||
function _eraIndexForStart(
|
||||
uint32 startTimestamp
|
||||
) internal pure returns (uint32) {
|
||||
return (startTimestamp - GENESIS_REWARDS_TIMESTAMP) / TEST_CALCULATION_INTERVAL;
|
||||
}
|
||||
|
||||
// ============ Configuration Tests ============
|
||||
|
||||
function test_setRewardsInitiator() public {
|
||||
|
|
@ -126,7 +132,7 @@ contract RewardsSubmitterTest is AVSDeployer {
|
|||
|
||||
vm.prank(operator1);
|
||||
vm.expectRevert(abi.encodeWithSignature("OnlyRewardsInitiator()"));
|
||||
serviceManager.submitRewards(submission);
|
||||
serviceManager.submitRewards(_eraIndexForStart(submission.startTimestamp), submission);
|
||||
}
|
||||
|
||||
// ============ Success Tests ============
|
||||
|
|
@ -143,7 +149,7 @@ contract RewardsSubmitterTest is AVSDeployer {
|
|||
vm.prank(snowbridgeAgent);
|
||||
vm.expectEmit(false, false, false, true);
|
||||
emit IDataHavenServiceManagerEvents.RewardsSubmitted(rewardAmount, 1);
|
||||
serviceManager.submitRewards(submission);
|
||||
serviceManager.submitRewards(_eraIndexForStart(submission.startTimestamp), submission);
|
||||
}
|
||||
|
||||
function test_submitRewards_multipleOperators() public {
|
||||
|
|
@ -190,7 +196,7 @@ contract RewardsSubmitterTest is AVSDeployer {
|
|||
vm.prank(snowbridgeAgent);
|
||||
vm.expectEmit(false, false, false, true);
|
||||
emit IDataHavenServiceManagerEvents.RewardsSubmitted(totalAmount, 2);
|
||||
serviceManager.submitRewards(submission);
|
||||
serviceManager.submitRewards(_eraIndexForStart(submission.startTimestamp), submission);
|
||||
}
|
||||
|
||||
function test_submitRewards_multipleSubmissions() public {
|
||||
|
|
@ -203,7 +209,7 @@ contract RewardsSubmitterTest is AVSDeployer {
|
|||
submission0.startTimestamp = GENESIS_REWARDS_TIMESTAMP;
|
||||
vm.warp(submission0.startTimestamp + duration + 1);
|
||||
vm.prank(snowbridgeAgent);
|
||||
serviceManager.submitRewards(submission0);
|
||||
serviceManager.submitRewards(_eraIndexForStart(submission0.startTimestamp), submission0);
|
||||
|
||||
// Submit for period 1
|
||||
IRewardsCoordinatorTypes.OperatorDirectedRewardsSubmission memory submission1 =
|
||||
|
|
@ -211,7 +217,7 @@ contract RewardsSubmitterTest is AVSDeployer {
|
|||
submission1.startTimestamp = GENESIS_REWARDS_TIMESTAMP + duration;
|
||||
vm.warp(submission1.startTimestamp + duration + 1);
|
||||
vm.prank(snowbridgeAgent);
|
||||
serviceManager.submitRewards(submission1);
|
||||
serviceManager.submitRewards(_eraIndexForStart(submission1.startTimestamp), submission1);
|
||||
|
||||
// Submit for period 2
|
||||
IRewardsCoordinatorTypes.OperatorDirectedRewardsSubmission memory submission2 =
|
||||
|
|
@ -219,7 +225,32 @@ contract RewardsSubmitterTest is AVSDeployer {
|
|||
submission2.startTimestamp = GENESIS_REWARDS_TIMESTAMP + 2 * duration;
|
||||
vm.warp(submission2.startTimestamp + duration + 1);
|
||||
vm.prank(snowbridgeAgent);
|
||||
serviceManager.submitRewards(submission2);
|
||||
serviceManager.submitRewards(_eraIndexForStart(submission2.startTimestamp), submission2);
|
||||
}
|
||||
|
||||
function test_submitRewards_revertsIfEraAlreadySubmittedForToken() public {
|
||||
_registerOperator(operator1, operator1);
|
||||
IRewardsCoordinatorTypes.OperatorDirectedRewardsSubmission memory submission =
|
||||
_buildSubmission(1000e18, operator1);
|
||||
uint32 eraIndex = _eraIndexForStart(submission.startTimestamp);
|
||||
|
||||
vm.warp(submission.startTimestamp + submission.duration + 1);
|
||||
|
||||
vm.prank(snowbridgeAgent);
|
||||
serviceManager.submitRewards(eraIndex, submission);
|
||||
|
||||
assertTrue(
|
||||
serviceManager.rewardsSubmittedForEra(eraIndex, address(rewardToken)),
|
||||
"replay guard should be set for the submitted era and token"
|
||||
);
|
||||
|
||||
vm.prank(snowbridgeAgent);
|
||||
vm.expectRevert(
|
||||
abi.encodeWithSignature(
|
||||
"RewardsAlreadySubmittedForEra(uint32,address)", eraIndex, address(rewardToken)
|
||||
)
|
||||
);
|
||||
serviceManager.submitRewards(eraIndex, submission);
|
||||
}
|
||||
|
||||
function test_submitRewards_withCustomDescription() public {
|
||||
|
|
@ -249,11 +280,14 @@ contract RewardsSubmitterTest is AVSDeployer {
|
|||
vm.warp(submission.startTimestamp + submission.duration + 1);
|
||||
|
||||
vm.prank(snowbridgeAgent);
|
||||
serviceManager.submitRewards(submission);
|
||||
serviceManager.submitRewards(_eraIndexForStart(submission.startTimestamp), submission);
|
||||
}
|
||||
|
||||
function test_submitRewards_withDifferentToken() public {
|
||||
_registerOperator(operator1, operator1);
|
||||
IRewardsCoordinatorTypes.OperatorDirectedRewardsSubmission memory firstSubmission =
|
||||
_buildSubmission(1000e18, operator1);
|
||||
|
||||
// Deploy a different token
|
||||
ERC20FixedSupply otherToken =
|
||||
new ERC20FixedSupply("Other", "OTHER", 1000000e18, address(this));
|
||||
|
|
@ -282,11 +316,24 @@ contract RewardsSubmitterTest is AVSDeployer {
|
|||
});
|
||||
|
||||
vm.warp(submission.startTimestamp + submission.duration + 1);
|
||||
uint32 eraIndex = _eraIndexForStart(submission.startTimestamp);
|
||||
|
||||
vm.prank(snowbridgeAgent);
|
||||
serviceManager.submitRewards(eraIndex, firstSubmission);
|
||||
|
||||
vm.prank(snowbridgeAgent);
|
||||
vm.expectEmit(false, false, false, true);
|
||||
emit IDataHavenServiceManagerEvents.RewardsSubmitted(500e18, 1);
|
||||
serviceManager.submitRewards(submission);
|
||||
serviceManager.submitRewards(eraIndex, submission);
|
||||
|
||||
assertTrue(
|
||||
serviceManager.rewardsSubmittedForEra(eraIndex, address(rewardToken)),
|
||||
"original token should be marked as submitted for the era"
|
||||
);
|
||||
assertTrue(
|
||||
serviceManager.rewardsSubmittedForEra(eraIndex, address(otherToken)),
|
||||
"different token should be independently tracked for the same era"
|
||||
);
|
||||
}
|
||||
|
||||
function test_submitRewards_translatesSolochainOperatorToEthOperator() public {
|
||||
|
|
@ -345,7 +392,7 @@ contract RewardsSubmitterTest is AVSDeployer {
|
|||
"submission should use solochain operator"
|
||||
);
|
||||
vm.prank(snowbridgeAgent);
|
||||
serviceManager.submitRewards(submission);
|
||||
serviceManager.submitRewards(_eraIndexForStart(submission.startTimestamp), submission);
|
||||
}
|
||||
|
||||
function test_submitRewards_skipsUnknownSolochainAddress() public {
|
||||
|
|
@ -357,7 +404,7 @@ contract RewardsSubmitterTest is AVSDeployer {
|
|||
vm.prank(snowbridgeAgent);
|
||||
vm.expectEmit();
|
||||
emit IDataHavenServiceManagerEvents.RewardsSubmitted(0, 0);
|
||||
serviceManager.submitRewards(submission);
|
||||
serviceManager.submitRewards(_eraIndexForStart(submission.startTimestamp), submission);
|
||||
}
|
||||
|
||||
function test_submitRewards_sortsTranslatedOperatorsByAddress() public {
|
||||
|
|
@ -437,6 +484,6 @@ contract RewardsSubmitterTest is AVSDeployer {
|
|||
vm.prank(snowbridgeAgent);
|
||||
vm.expectEmit(false, false, false, true);
|
||||
emit IDataHavenServiceManagerEvents.RewardsSubmitted(totalAmount, 2);
|
||||
serviceManager.submitRewards(submission);
|
||||
serviceManager.submitRewards(_eraIndexForStart(submission.startTimestamp), submission);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ sol! {
|
|||
}
|
||||
|
||||
/// The submitRewards function on DataHavenServiceManager.
|
||||
function submitRewards(OperatorDirectedRewardsSubmission submission);
|
||||
function submitRewards(uint32 eraIndex, OperatorDirectedRewardsSubmission submission);
|
||||
}
|
||||
|
||||
/// Configuration for rewards submission.
|
||||
|
|
@ -189,6 +189,7 @@ fn build_rewards_message<C: RewardsSubmissionConfig>(
|
|||
strategies_and_multipliers.sort_by_key(|(strategy, _)| *strategy);
|
||||
|
||||
let calldata = encode_rewards_calldata(
|
||||
rewards_utils.era_index,
|
||||
whave_token_address,
|
||||
&strategies_and_multipliers,
|
||||
&operator_rewards,
|
||||
|
|
@ -269,7 +270,7 @@ pub fn points_to_rewards(
|
|||
/// ABI-encode the submitRewards calldata for DataHavenServiceManager.
|
||||
///
|
||||
/// Uses alloy's type-safe ABI encoding to generate the calldata for
|
||||
/// `submitRewards(OperatorDirectedRewardsSubmission)`.
|
||||
/// `submitRewards(uint32,OperatorDirectedRewardsSubmission)`.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `token` - ERC20 reward token address
|
||||
|
|
@ -283,6 +284,7 @@ pub fn points_to_rewards(
|
|||
/// `Ok(Vec<u8>)` with the ABI-encoded calldata, or `Err` if encoding fails
|
||||
/// (e.g., multiplier exceeds uint96 max).
|
||||
pub fn encode_rewards_calldata(
|
||||
era_index: u32,
|
||||
token: H160,
|
||||
strategies_and_multipliers: &[(H160, u128)],
|
||||
operator_rewards: &[(H160, u128)],
|
||||
|
|
@ -331,7 +333,11 @@ pub fn encode_rewards_calldata(
|
|||
description: description.into(),
|
||||
};
|
||||
|
||||
Ok(submitRewardsCall { submission }.abi_encode())
|
||||
Ok(submitRewardsCall {
|
||||
eraIndex: era_index,
|
||||
submission,
|
||||
}
|
||||
.abi_encode())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -637,8 +643,9 @@ mod tests {
|
|||
#[test]
|
||||
fn test_encode_submit_rewards_calldata_selector() {
|
||||
// Verify the function selector matches the expected value
|
||||
// cast sig "submitRewards(((address,uint96)[],address,(address,uint256)[],uint32,uint32,string))" = 0x83821e8e
|
||||
// cast sig "submitRewards(uint32,((address,uint96)[],address,(address,uint256)[],uint32,uint32,string))" = 0x61115ba2
|
||||
let calldata = encode_rewards_calldata(
|
||||
7,
|
||||
H160::from_low_u64_be(0x1234),
|
||||
&[],
|
||||
&[(H160::from_low_u64_be(0x5678), 1000)],
|
||||
|
|
@ -649,7 +656,7 @@ mod tests {
|
|||
.expect("Encoding should succeed");
|
||||
|
||||
// Check the function selector (first 4 bytes)
|
||||
assert_eq!(&calldata[0..4], &[0x83, 0x82, 0x1e, 0x8e]);
|
||||
assert_eq!(&calldata[0..4], &[0x61, 0x11, 0x5b, 0xa2]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -658,6 +665,7 @@ mod tests {
|
|||
let invalid_multiplier = MAX_UINT96 + 1;
|
||||
|
||||
let result = encode_rewards_calldata(
|
||||
7,
|
||||
H160::from_low_u64_be(0x1234),
|
||||
&[(H160::from_low_u64_be(0x9999), invalid_multiplier)],
|
||||
&[(H160::from_low_u64_be(0x5678), 1000u128)],
|
||||
|
|
@ -676,11 +684,13 @@ mod tests {
|
|||
let multiplier = (1u128 << 80) + 123u128;
|
||||
let operator = H160::from_low_u64_be(0x5678);
|
||||
let amount = 1000u128;
|
||||
let era_index = 7u32;
|
||||
let start_timestamp = 86_400u32;
|
||||
let duration = 86_400u32;
|
||||
let description = "round trip";
|
||||
|
||||
let calldata = encode_rewards_calldata(
|
||||
era_index,
|
||||
token,
|
||||
&[(strategy, multiplier)],
|
||||
&[(operator, amount)],
|
||||
|
|
@ -691,6 +701,7 @@ mod tests {
|
|||
.expect("Encoding should succeed");
|
||||
|
||||
let decoded = submitRewardsCall::abi_decode(&calldata, true).expect("Decoding should work");
|
||||
assert_eq!(decoded.eraIndex, era_index);
|
||||
let submission = decoded.submission;
|
||||
|
||||
assert_eq!(submission.token, Address::from(token.as_fixed_bytes()));
|
||||
|
|
@ -718,11 +729,19 @@ mod tests {
|
|||
expected_multiplier_u96
|
||||
);
|
||||
|
||||
let empty_calldata =
|
||||
encode_rewards_calldata(token, &[], &[], start_timestamp, duration, "empty")
|
||||
.expect("Encoding should succeed");
|
||||
let empty_calldata = encode_rewards_calldata(
|
||||
era_index,
|
||||
token,
|
||||
&[],
|
||||
&[],
|
||||
start_timestamp,
|
||||
duration,
|
||||
"empty",
|
||||
)
|
||||
.expect("Encoding should succeed");
|
||||
let empty_decoded =
|
||||
submitRewardsCall::abi_decode(&empty_calldata, true).expect("Decoding should work");
|
||||
assert_eq!(empty_decoded.eraIndex, era_index);
|
||||
let empty_submission = empty_decoded.submission;
|
||||
|
||||
assert_eq!(
|
||||
|
|
@ -769,6 +788,7 @@ mod tests {
|
|||
.0;
|
||||
|
||||
let expected_calldata = encode_rewards_calldata(
|
||||
rewards_utils.era_index,
|
||||
HappyPathConfig::whave_token_address(),
|
||||
&HappyPathConfig::strategies_and_multipliers(),
|
||||
&expected_operator_rewards,
|
||||
|
|
@ -817,6 +837,7 @@ mod tests {
|
|||
.expect("Expected message to be built");
|
||||
|
||||
let expected_calldata = encode_rewards_calldata(
|
||||
rewards_utils.era_index,
|
||||
HappyPathConfig::whave_token_address(),
|
||||
&HappyPathConfig::strategies_and_multipliers(),
|
||||
&operator_rewards,
|
||||
|
|
|
|||
|
|
@ -2232,6 +2232,16 @@ export const dataHavenServiceManagerAbi = [
|
|||
outputs: [{ name: '', internalType: 'address', type: 'address' }],
|
||||
stateMutability: 'view',
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
inputs: [
|
||||
{ name: '', internalType: 'uint32', type: 'uint32' },
|
||||
{ name: '', internalType: 'address', type: 'address' },
|
||||
],
|
||||
name: 'rewardsSubmittedForEra',
|
||||
outputs: [{ name: '', internalType: 'bool', type: 'bool' }],
|
||||
stateMutability: 'view',
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
inputs: [
|
||||
|
|
@ -2335,6 +2345,7 @@ export const dataHavenServiceManagerAbi = [
|
|||
{
|
||||
type: 'function',
|
||||
inputs: [
|
||||
{ name: 'eraIndex', internalType: 'uint32', type: 'uint32' },
|
||||
{
|
||||
name: 'submission',
|
||||
internalType:
|
||||
|
|
@ -2710,6 +2721,14 @@ export const dataHavenServiceManagerAbi = [
|
|||
{ type: 'error', inputs: [], name: 'OperatorAlreadyRegistered' },
|
||||
{ type: 'error', inputs: [], name: 'OperatorNotInAllowlist' },
|
||||
{ type: 'error', inputs: [], name: 'OperatorNotRegistered' },
|
||||
{
|
||||
type: 'error',
|
||||
inputs: [
|
||||
{ name: 'eraIndex', internalType: 'uint32', type: 'uint32' },
|
||||
{ name: 'token', internalType: 'address', type: 'address' },
|
||||
],
|
||||
name: 'RewardsAlreadySubmittedForEra',
|
||||
},
|
||||
{ type: 'error', inputs: [], name: 'SolochainAddressAlreadyAssigned' },
|
||||
{ type: 'error', inputs: [], name: 'StrategyNotInOperatorSet' },
|
||||
{ type: 'error', inputs: [], name: 'UnknownSolochainAddress' },
|
||||
|
|
@ -11030,6 +11049,15 @@ export const readDataHavenServiceManagerRewardsInitiator =
|
|||
functionName: 'rewardsInitiator',
|
||||
})
|
||||
|
||||
/**
|
||||
* Wraps __{@link readContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"rewardsSubmittedForEra"`
|
||||
*/
|
||||
export const readDataHavenServiceManagerRewardsSubmittedForEra =
|
||||
/*#__PURE__*/ createReadContract({
|
||||
abi: dataHavenServiceManagerAbi,
|
||||
functionName: 'rewardsSubmittedForEra',
|
||||
})
|
||||
|
||||
/**
|
||||
* Wraps __{@link readContract}__ with `abi` set to __{@link dataHavenServiceManagerAbi}__ and `functionName` set to `"snowbridgeGateway"`
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in a new issue