Skip to content

Commit ab248e5

Browse files
authored
Merge pull request #1 from blocknative/multipayload
multi-payload
2 parents becd58c + c086ae1 commit ab248e5

9 files changed

Lines changed: 181 additions & 158 deletions

File tree

abi/RewardController.abi

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

contracts/RewardController.vy

Lines changed: 116 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33

44
interface IOracle:
55
def get(systemid: uint8, cid: uint64, typ: uint16) -> (uint256, uint64, uint48): view
6-
#def storeValues(dat: DynArray[uint8, 256]): nonpayable
76
def storeValues(dat: Bytes[16384]): nonpayable
7+
def storeValuesWithReceipt(dat: Bytes[16384]) -> DynArray[RecordReceipt, MAX_PAYLOADS]: nonpayable
88

99
event OracleUpdated:
1010
updater: address
1111
chain_id: uint64
12-
new_value: uint256
12+
new_value: uint240
1313
deviation: uint256
1414
time_since: uint256
1515
time_reward: uint256
@@ -35,12 +35,19 @@ struct Reward:
3535
time_reward: uint256
3636
deviation_reward: uint256
3737

38+
struct EnhancedReward:
39+
chain_id: uint64
40+
height: uint64
41+
gas_price: uint240
42+
time_reward: uint256
43+
deviation_reward: uint256
44+
3845
struct TotalRewards:
3946
updater: address
4047
total_rewards: uint256
4148

4249
BASEFEE_REWARD_TYPE: public(constant(uint16)) = 107
43-
MAX_PAYLOADS: public(constant(uint16)) = 64
50+
MAX_PAYLOADS: public(constant(uint256)) = 32
4451
MAX_UPDATERS: public(constant(uint32)) = 2**16
4552
MAX_PAYLOAD_SIZE: public(constant(uint256)) = 16384
4653

@@ -67,12 +74,8 @@ n_updaters: public(uint32)
6774
frozen: public(bool)
6875
oracle: public(IOracle)
6976

70-
#error_integral: public(int256)
7177
error_integral: public(HashMap[uint64, int256])
7278
last_output: public(HashMap[uint64, int256])
73-
#last_p_output: public(HashMap[uint64, int256])
74-
#last_i_output: public(HashMap[uint64, int256])
75-
#last_update_time: public(HashMap[uint64, uint256])
7679

7780
rewards: public(HashMap[address, uint256])
7881
total_rewards: public(uint256)
@@ -83,7 +86,6 @@ index: HashMap[uint64, uint256] # Pointer to next insert position (0 to N-1)
8386
count: HashMap[uint64, uint256] # Number of elements inserted so far, up to N
8487
rolling_sum: HashMap[uint64, uint256] # Sum of last N values for efficient averaging
8588

86-
#coeff: public(int96[4])
8789
coeff: public(Coefficients)
8890
intercept: public(int256)
8991

@@ -260,7 +262,6 @@ def _decode(dat: Bytes[MAX_PAYLOAD_SIZE], tip_typ: uint16) -> (uint8, uint64, ui
260262

261263
return sid, cid, basefee_val, tip_val, ts, h
262264

263-
264265
@internal
265266
@view
266267
def _riemann_sum(x: int256, y: int256)-> int256:
@@ -397,130 +398,120 @@ def get_updaters_chunk(start: uint256, count: uint256) -> (address[256], uint256
397398

398399
return result_updaters, result_rewards
399400

400-
@external
401-
def update_oracles(dat_many: Bytes[MAX_PAYLOAD_SIZE], n: uint256)-> Reward[MAX_PAYLOADS]:
402-
return self._update_oracles(dat_many, n)
403-
404-
@internal
405-
def _update_oracles(dat_many: Bytes[MAX_PAYLOAD_SIZE], n: uint256)-> Reward[MAX_PAYLOADS]:
406-
assert not self.frozen, "rewards contract is frozen"
407-
self._add_updater(msg.sender)
408-
offset: uint256 = 0
409-
plen: uint16 = 0
410-
411-
time_reward: uint256 = 0
412-
deviation_reward: uint256 = 0
413-
414-
rewards: Reward[MAX_PAYLOADS] = empty(Reward[MAX_PAYLOADS])
415-
416-
dat_p: Bytes[MAX_PAYLOAD_SIZE] = b""
417-
l: uint256 = len(dat_many)
418-
419-
for i: uint256 in range(n, bound=16):
420-
dat_p = slice(dat_many, offset, l-offset)
421-
plen = self._decode_plen(dat_p)
422-
if plen == 0:
423-
assert i == n - 1, "plen is zero before n is reached"
424-
break
425-
426-
payload_size: uint256 = 32 + convert(plen, uint256)*32 + 65
427-
time_reward, deviation_reward = self._update_oracle(dat_p, payload_size)
428-
#time_reward, deviation_reward = self._update_oracle(slice(dat_p, offset, offset + payload_size), payload_size)
429-
rewards[i] = Reward(time_reward=time_reward, deviation_reward=deviation_reward)
430-
431-
# add full payload size
432-
offset += 32 + convert(plen, uint256)*32 + 65
433-
434-
return rewards
401+
struct RecordReceipt:
402+
systemid: uint8
403+
cid: uint64
404+
typ: uint16
405+
old_height: uint64
406+
old_timestamp: uint48
407+
old_value: uint240
408+
new_height: uint64
409+
new_timestamp: uint48
410+
new_value: uint240
435411

436412
@external
437-
def update_oracle(dat: Bytes[MAX_PAYLOAD_SIZE])-> Reward:
438-
assert not self.frozen, "rewards contract is frozen"
439-
self._add_updater(msg.sender)
440-
return self._update_oracles(dat, 1)[0]
441-
442-
@internal
443-
def copy_bytes_to_dynarray(src: Bytes[MAX_PAYLOAD_SIZE], start: uint256, length: uint256) -> DynArray[uint8, MAX_PAYLOAD_SIZE]:
444-
assert start + length <= len(src), "out of bounds"
445-
446-
result: DynArray[uint8, MAX_PAYLOAD_SIZE] = []
447-
for i: uint256 in range(length, bound=MAX_PAYLOAD_SIZE):
448-
b: uint8 = convert(slice(src, start + i, 1), uint8)
449-
result.append(b)
450-
451-
return result
452-
453-
@internal
454-
def _update_oracle_stub(dat: Bytes[MAX_PAYLOAD_SIZE], l: uint256)-> (uint256, uint256):
455-
tip_typ: uint16 = self.tip_reward_type
456-
return 0, 0
457-
458-
@internal
459-
def _update_oracle(dat: Bytes[MAX_PAYLOAD_SIZE], l: uint256)-> (uint256, uint256):
460-
tip_typ: uint16 = self.tip_reward_type
461-
sid: uint8 = 0
413+
def update_many(dat: Bytes[MAX_PAYLOAD_SIZE])-> EnhancedReward[MAX_PAYLOADS]:
414+
receipts: DynArray[RecordReceipt, MAX_PAYLOADS] = extcall self.oracle.storeValuesWithReceipt(dat)
415+
rewards: EnhancedReward[MAX_PAYLOADS] = empty(EnhancedReward[MAX_PAYLOADS])
462416
cid: uint64 = 0
463417
typ: uint16 = 0
464-
new_basefee_value: uint240 = 0
465-
new_tip_value: uint240 = 0
466-
new_ts: uint48 = 0
467-
new_height: uint64 = 0
468-
469-
# decode data and get new values
470-
sid, cid, new_basefee_value, new_tip_value, new_ts, new_height = self._decode(dat, tip_typ)
471-
472-
new_tip_value_u: uint256 = convert(new_tip_value, uint256)
473-
new_basefee_value_u: uint256 = convert(new_basefee_value, uint256)
474-
new_gasprice_value: uint256 = new_basefee_value_u + new_tip_value_u
475-
476-
current_gasprice_value: uint256 = 0
477-
current_basefee_value: uint256 = 0
478-
current_tip_value: uint256 = 0
479-
current_height: uint64 = 0
480-
current_ts: uint48 = 0
481-
482-
# current oracle values
483-
(current_basefee_value, current_height, current_ts) = staticcall self.oracle.get(sid, cid, BASEFEE_REWARD_TYPE)
484-
(current_tip_value, current_height, current_ts) = staticcall self.oracle.get(sid, cid, tip_typ)
485-
current_gasprice_value = current_basefee_value + current_tip_value
486-
487-
if not (new_height > current_height or (new_height == current_height and new_ts > current_ts)):
488-
return 0, 0
489-
490-
# calculate deviation and staleness(time_since) for new values
491-
deviation: uint256 = self._calc_deviation(cid, new_gasprice_value, current_gasprice_value)
492-
time_since: uint256 = convert(new_ts - current_ts, uint256) * EIGHTEEN_DECIMAL_NUMBER_U
493-
494-
# calculate reward
418+
rec: RecordReceipt = empty(RecordReceipt)
419+
old_tip_val: uint240 = 0
420+
old_bf_val: uint240 = 0
421+
new_tip_val: uint240 = 0
422+
new_bf_val: uint240 = 0
423+
424+
bf_found: bool = False
425+
tip_found: bool = False
426+
427+
current_gasprice: uint240 = 0
428+
new_gasprice: uint240 = 0
429+
deviation: uint256 = 0
430+
time_since: uint256 = 0
495431
time_reward: int256 = 0
496432
deviation_reward: int256 = 0
497-
time_reward, deviation_reward = self._calc_reward(convert(time_since, int256)//1000, convert(deviation, int256))
498-
499-
# calculate reward multiplier
500-
reward_mult: int256 = self._calc_reward_mult(cid, time_since//1000)
501-
502-
# adjust rewards with multiplier
503-
time_reward_adj: int256 = reward_mult * time_reward // EIGHTEEN_DECIMAL_NUMBER
504-
deviation_reward_adj: int256 = reward_mult * deviation_reward // EIGHTEEN_DECIMAL_NUMBER
505-
506-
time_reward_adj_u: uint256 = convert(time_reward_adj, uint256)
507-
deviation_reward_adj_u: uint256 = convert(deviation_reward_adj, uint256)
508-
509-
# store rewards
510-
self.rewards[msg.sender] += time_reward_adj_u + deviation_reward_adj_u
511-
self.total_rewards += time_reward_adj_u + deviation_reward_adj_u
512-
513-
log OracleUpdated(updater=msg.sender, chain_id=cid, new_value=new_gasprice_value,
514-
deviation=deviation, time_since=time_since,
515-
time_reward=time_reward_adj_u, deviation_reward=deviation_reward_adj_u,
516-
reward_mult=reward_mult)
433+
reward_mult: int256 = 0
434+
time_reward_adj: int256 = 0
435+
deviation_reward_adj: int256 = 0
436+
time_reward_adj_u: uint256 = 0
437+
deviation_reward_adj_u: uint256 = 0
438+
439+
# self.tip_reward_type
440+
# BASEFEE_REWARD_TYPE
441+
idx: uint256 = 0
442+
for i: uint256 in range(len(receipts), bound=MAX_PAYLOADS):
443+
rec = receipts[i]
444+
445+
if rec.typ == self.tip_reward_type:
446+
old_tip_val = rec.old_value
447+
new_tip_val = rec.new_value
448+
tip_found = True
449+
elif rec.typ == BASEFEE_REWARD_TYPE:
450+
old_bf_val = rec.old_value
451+
new_bf_val = rec.new_value
452+
bf_found = True
453+
454+
# tip and bf found for this cid, time to process
455+
if not (tip_found and bf_found):
456+
continue
457+
458+
# reset these
459+
tip_found = False
460+
bf_found = False
461+
462+
current_gasprice = old_tip_val + old_bf_val
463+
464+
# no update
465+
if rec.old_timestamp == rec.new_timestamp:
466+
rewards[idx] = EnhancedReward(chain_id=rec.cid,
467+
height=rec.old_height,
468+
gas_price=current_gasprice,
469+
time_reward=0,
470+
deviation_reward=0)
471+
continue
472+
473+
new_gasprice = new_tip_val + new_bf_val
474+
475+
# calculate deviation and staleness(time_since) for new values
476+
deviation = self._calc_deviation(rec.cid, convert(new_gasprice, uint256), convert(current_gasprice, uint256))
477+
478+
time_since = convert(rec.new_timestamp - rec.old_timestamp, uint256) * EIGHTEEN_DECIMAL_NUMBER_U
479+
480+
# calculate reward
481+
time_reward, deviation_reward = self._calc_reward(convert(time_since, int256)//1000, convert(deviation, int256))
482+
483+
# calculate reward multiplier
484+
reward_mult = self._calc_reward_mult(rec.cid, time_since//1000)
485+
486+
# adjust rewards with multiplier
487+
time_reward_adj = reward_mult * time_reward // EIGHTEEN_DECIMAL_NUMBER
488+
deviation_reward_adj = reward_mult * deviation_reward // EIGHTEEN_DECIMAL_NUMBER
489+
490+
time_reward_adj_u = convert(time_reward_adj, uint256)
491+
deviation_reward_adj_u = convert(deviation_reward_adj, uint256)
492+
493+
# store rewards
494+
self.rewards[msg.sender] += time_reward_adj_u + deviation_reward_adj_u
495+
self.total_rewards += time_reward_adj_u + deviation_reward_adj_u
496+
497+
log OracleUpdated(updater=msg.sender, chain_id=rec.cid, new_value=new_gasprice,
498+
deviation=deviation, time_since=time_since,
499+
time_reward=time_reward_adj_u, deviation_reward=deviation_reward_adj_u,
500+
reward_mult=reward_mult)
501+
502+
rewards[idx] = EnhancedReward(chain_id=rec.cid,
503+
height=rec.old_height,
504+
gas_price=current_gasprice,
505+
time_reward=time_reward_adj_u,
506+
deviation_reward=deviation_reward_adj_u)
507+
idx += 1
517508

518-
# send new values to oracle
519-
#extcall self.oracle.storeValues(dat)
520-
extcall self.oracle.storeValues(slice(dat, 0, l))
521-
#extcall self.oracle.storeValues(self.copy_bytes_to_dynarray(dat, 0, l))
509+
return rewards
522510

523-
return time_reward_adj_u, deviation_reward_adj_u
511+
@internal
512+
def _update_oracle_stub(dat: Bytes[MAX_PAYLOAD_SIZE], l: uint256)-> (uint256, uint256):
513+
tip_typ: uint16 = self.tip_reward_type
514+
return 0, 0
524515

525516
@external
526517
@view
@@ -575,10 +566,6 @@ def _get_window_size(chain_id: uint64) -> uint256:
575566
return self.default_window_size
576567
return value
577568

578-
#@external
579-
#def add_value(chain_id: uint64, new_value: uint256, count: uint256, window_size: uint256):
580-
# self._add_value(chain_id, new_value, count, window_size)
581-
582569
@internal
583570
def _add_value(chain_id: uint64, new_value: uint256, count: uint256, window_size: uint256):
584571
#Add a new value to the circular buffer and update rolling sum.
@@ -666,22 +653,12 @@ def _update(cid: uint64, error: int256) -> (int256, int256, int256):
666653

667654
bounded_pi_output: int256 = self._bound_pi_output(pi_output)
668655

669-
#self.error_integral[cid] = self._clamp_error_integral(cid, bounded_pi_output, new_error_integral, new_area)
670656
self.error_integral[cid] = self._clamp_error_integral(bounded_pi_output, error_integral, new_error_integral, error)
671657

672-
# could maybe remove these to save gas
673-
#self.last_update_time[cid] = block.timestamp
674658
self.last_output[cid] = bounded_pi_output
675-
#self.last_p_output[cid] = p_output
676-
#self.last_i_output[cid] = i_output
677659

678660
return (bounded_pi_output, p_output, i_output)
679661

680-
#@external
681-
#@view
682-
#def last_update(cid: uint64) -> (uint256, int256, int256, int256):
683-
# return (self.last_update_time[cid], self.last_output[cid], self.last_p_output[cid], self.last_i_output[cid])
684-
685662
@external
686663
@view
687664
def get_new_pi_output(cid: uint64, error: int256) -> (int256, int256, int256):
@@ -702,8 +679,3 @@ def _get_new_pi_output(cid: uint64, error: int256) -> (int256, int256, int256):
702679
bounded_pi_output: int256 = self._bound_pi_output(pi_output)
703680

704681
return (bounded_pi_output, p_output, i_output)
705-
706-
#@external
707-
#@view
708-
#def elapsed(cid: uint64) -> uint256:
709-
# return block.timestamp - self.last_update_time[cid]

scripts/oracles.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Blocknative Gas Oracles on each chain
22

3-
oracle_addresses = {11155111: '0xCc936bE977BeDb5140C5584d8B6043C9068622A6'}
3+
oracle_addresses = {11155111: '0x954d74dD0383084fDdD79B860bE0Ec2eC3f01129'}

scripts/set_scales.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from scripts import params
1414
from scripts.oracles import oracle_addresses
1515

16-
REWARDS = '0x812cb53503f7232574cb6900ccbd58dd551f3300'
16+
REWARDS = '0xd1aC001f243E61682167D1177fAd4182C956E493'
1717
controller = project.RewardController.at(REWARDS)
1818

1919
def set_scales(account, controller, params):

scripts/update_multi.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,9 @@
1414
from scripts.oracles import oracle_addresses
1515

1616
SEPOLIA_ORACLE = oracle_addresses[11155111]#'0xCc936bE977BeDb5140C5584d8B6043C9068622A6'
17-
REWARDS = '0x812cb53503f7232574cb6900ccbd58dd551f3300'
17+
REWARDS = '0xd1aC001f243E61682167D1177fAd4182C956E493'
1818
controller = project.RewardController.at(REWARDS)
1919

20-
oracle_sepolia = Contract(SEPOLIA_ORACLE, abi=gas_oracle_v2_abi)
21-
2220
# Gasnet
2321
w3 = Web3(HTTPProvider('https://rpc.gas.network'))
2422
address = '0x4245Cb7c690B650a38E680C9BcfB7814D42BfD32'
@@ -41,14 +39,15 @@
4139
# read gasnet
4240
dat_1: bytes = oracle_gasnet.functions.getValues(sid, cid_1).call()
4341
dat_2: bytes = oracle_gasnet.functions.getValues(sid, cid_2).call()
44-
dat = dat_1 + dat_2
42+
d = b'0000000000000000000000000000000'
43+
dat = dat_1 + d + dat_2
4544

46-
rewards = controller.update_oracles.call(dat, 2)
45+
rewards = controller.update_many.call(dat)
4746
print("pending rewards")
4847
print(rewards)
4948

5049
# update oracle w/ gasnet payload
51-
tx = controller.update_oracles(dat, 2, sender=account, raise_on_revert=True, gas=3000000)
50+
tx = controller.update_many(dat, sender=account, raise_on_revert=True, gas=3000000)
5251
tx.show_trace(True)
5352

5453
print(tx.events)

0 commit comments

Comments
 (0)