Skip to content

Commit df65549

Browse files
committed
feat: initial SEAMessenger implementation
1 parent a64b375 commit df65549

File tree

2 files changed

+259
-0
lines changed

2 files changed

+259
-0
lines changed
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
pragma solidity 0.6.6;
2+
3+
pragma experimental ABIEncoderV2;
4+
5+
import '@chainlink/contracts/src/v0.6/ChainlinkClient.sol';
6+
7+
import '@stacktical/dsla-contracts/contracts/interfaces/IMessenger.sol';
8+
import '@stacktical/dsla-contracts/contracts/SLA.sol';
9+
import '@stacktical/dsla-contracts/contracts/PeriodRegistry.sol';
10+
import '@stacktical/dsla-contracts/contracts/StringUtils.sol';
11+
import '@stacktical/dsla-contracts/contracts/StakeRegistry.sol';
12+
13+
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
14+
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
15+
16+
contract SEAMessenger is ChainlinkClient, IMessenger, ReentrancyGuard {
17+
using SafeERC20 for ERC20;
18+
19+
mapping(bytes32 => SLIRequest) public requestIdToSLIRequest;
20+
bytes32[] public requests;
21+
address private _slaRegistryAddress;
22+
address private immutable _oracle;
23+
bytes32 private _jobId;
24+
uint256 private constant _baseFee = 0.1 ether;
25+
uint256 private _fee;
26+
uint256 private constant _messengerPrecision = 10**3;
27+
28+
uint256 private _requestsCounter;
29+
uint256 private _fulfillsCounter;
30+
PeriodRegistry private periodRegistry;
31+
StakeRegistry private stakeRegistry;
32+
bool private retry = false;
33+
bytes32 public networkName;
34+
35+
constructor(
36+
address _messengerChainlinkOracle,
37+
address _messengerChainlinkToken,
38+
uint256 _feeMultiplier,
39+
PeriodRegistry _periodRegistry,
40+
StakeRegistry _stakeRegistry,
41+
bytes32 _networkName
42+
) public {
43+
setChainlinkToken(_messengerChainlinkToken);
44+
_oracle = _messengerChainlinkOracle;
45+
_fee = _feeMultiplier * _baseFee;
46+
periodRegistry = _periodRegistry;
47+
stakeRegistry = _stakeRegistry;
48+
networkName = _networkName;
49+
}
50+
51+
event JobIdModified(address indexed owner, bytes32 jobId, uint256 fee);
52+
53+
event SLIRequested(
54+
address indexed caller,
55+
uint256 requestsCounter,
56+
bytes32 requestId
57+
);
58+
59+
modifier onlySLARegistry() {
60+
if (!retry) {
61+
require(
62+
msg.sender == _slaRegistryAddress,
63+
'Can only be called by SLARegistry'
64+
);
65+
}
66+
_;
67+
}
68+
69+
modifier retryLock() {
70+
retry = true;
71+
_;
72+
retry = false;
73+
}
74+
75+
function setSLARegistry() public override {
76+
require(
77+
_slaRegistryAddress == address(0),
78+
'SLARegistry address has already been set'
79+
);
80+
81+
_slaRegistryAddress = msg.sender;
82+
}
83+
84+
function requestSLI(
85+
uint256 _periodId,
86+
address _slaAddress,
87+
bool _messengerOwnerApproval,
88+
address _callerAddress
89+
) public override onlySLARegistry nonReentrant {
90+
require(_jobId != 0, '_jobI empty');
91+
SLA sla = SLA(_slaAddress);
92+
if (_messengerOwnerApproval) {
93+
ERC20(chainlinkTokenAddress()).safeTransferFrom(
94+
owner(),
95+
address(this),
96+
_fee
97+
);
98+
} else {
99+
ERC20(chainlinkTokenAddress()).safeTransferFrom(
100+
_callerAddress,
101+
address(this),
102+
_fee
103+
);
104+
}
105+
Chainlink.Request memory request = buildChainlinkRequest(
106+
_jobId,
107+
address(this),
108+
this.fulfillSLI.selector
109+
);
110+
(
111+
uint256 sla_monitoring_start,
112+
uint256 sla_monitoring_end
113+
) = periodRegistry.getPeriodStartAndEnd(sla.periodType(), _periodId);
114+
request.add(
115+
'sla_monitoring_start',
116+
StringUtils.uintToStr(sla_monitoring_start)
117+
);
118+
request.add(
119+
'sla_monitoring_end',
120+
StringUtils.uintToStr(sla_monitoring_end)
121+
);
122+
request.add('sla_address', StringUtils.addressToString(_slaAddress));
123+
request.add('network_name', StringUtils.bytes32ToStr(networkName));
124+
125+
// Sends the request with 0.1 LINK to the oracle contract
126+
bytes32 requestId = sendChainlinkRequestTo(_oracle, request, _fee);
127+
128+
requests.push(requestId);
129+
130+
requestIdToSLIRequest[requestId] = SLIRequest({
131+
slaAddress: _slaAddress,
132+
periodId: _periodId
133+
});
134+
135+
_requestsCounter += 1;
136+
emit SLIRequested(_callerAddress, _requestsCounter, requestId);
137+
}
138+
139+
function fulfillSLI(bytes32 _requestId, uint256 _chainlinkResponse)
140+
external
141+
override
142+
nonReentrant
143+
recordChainlinkFulfillment(_requestId)
144+
{
145+
SLIRequest memory request = requestIdToSLIRequest[_requestId];
146+
emit SLIReceived(
147+
request.slaAddress,
148+
request.periodId,
149+
_requestId,
150+
bytes32(_chainlinkResponse)
151+
);
152+
_fulfillsCounter += 1;
153+
SLA(request.slaAddress).registerSLI(
154+
_chainlinkResponse,
155+
request.periodId
156+
);
157+
}
158+
159+
function retryRequest(address _slaAddress, uint256 _periodId)
160+
external
161+
override
162+
retryLock
163+
{
164+
require(
165+
stakeRegistry.periodIsVerified(_slaAddress, _periodId),
166+
'StakeRegistry: not verified'
167+
);
168+
SLA sla = SLA(_slaAddress);
169+
(, , SLA.Status status) = sla.periodSLIs(_periodId);
170+
require(status == SLA.Status.NotVerified, 'SLA: verified');
171+
requestSLI(_periodId, _slaAddress, false, msg.sender);
172+
}
173+
174+
function setChainlinkJobID(bytes32 _newJobId, uint256 _feeMultiplier)
175+
external
176+
override
177+
onlyOwner
178+
{
179+
_jobId = _newJobId;
180+
_fee = _feeMultiplier * _baseFee;
181+
emit JobIdModified(msg.sender, _newJobId, _fee);
182+
}
183+
184+
function slaRegistryAddress() external view override returns (address) {
185+
return _slaRegistryAddress;
186+
}
187+
188+
function messengerPrecision() external view override returns (uint256) {
189+
return _messengerPrecision;
190+
}
191+
192+
function oracle() external view override returns (address) {
193+
return _oracle;
194+
}
195+
196+
function jobId() external view override returns (bytes32) {
197+
return _jobId;
198+
}
199+
200+
function fee() external view override returns (uint256) {
201+
return _fee;
202+
}
203+
204+
function requestsCounter() external view override returns (uint256) {
205+
return _requestsCounter;
206+
}
207+
208+
function fulfillsCounter() external view override returns (uint256) {
209+
return _fulfillsCounter;
210+
}
211+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"useCaseName": "staking-efficiency-alt",
3+
"symbol": "SEA",
4+
"ownerUrl": "https://stacktical.com",
5+
"useCaseDocs": "https://readme.stacktical.com/developer-guide",
6+
"externalAdapterRepo": "https://github.com/Stacktical/stacktical-dsla-oracle-se-alt",
7+
"unit": "%",
8+
"keywords": ["blockchain", "cryptocurrency", "staking", "validator", "delegator", "uptime"],
9+
"timestamp": "2021-10-11T00:00:00+00:00",
10+
"ipfsData": {
11+
"serviceName": {
12+
"name": "Service Name",
13+
"type": "string",
14+
"description": "The Validator service name"
15+
},
16+
"serviceDescription": {
17+
"name": "Service Description",
18+
"type": "string",
19+
"description": "The Validator service description"
20+
},
21+
"serviceImage": {
22+
"name": "Service Image",
23+
"type": "url",
24+
"description": "The Validator service image url to display"
25+
},
26+
"serviceURL": {
27+
"name": "Service URL",
28+
"type": "url",
29+
"description": "The Validator service URL"
30+
},
31+
"serviceAddress": {
32+
"name": "Service Address",
33+
"type": "address",
34+
"description": "The Validator service blockchain address"
35+
},
36+
"serviceTicker": {
37+
"name": "Service Ticker",
38+
"type": "string",
39+
"description": "The Validator service ticker",
40+
"values": ["ONE", "DOT", "ATOM", "BAND", "eGLD", "XTZ", "AVAX", "ROSE"]
41+
}
42+
},
43+
"version": {
44+
"major": 1,
45+
"minor": 0,
46+
"patch": 0
47+
}
48+
}

0 commit comments

Comments
 (0)