-
Notifications
You must be signed in to change notification settings - Fork 11.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rosetta: Handle single GasCoin transfers #20592
base: main
Are you sure you want to change the base?
Changes from all commits
ad7295e
8120d1a
0b6a826
ce31fb1
b5fe666
69e3c59
f413381
7903670
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -558,6 +558,91 @@ impl Operations { | |
}; | ||
balance_change.chain(gas) | ||
} | ||
|
||
/// Checks to see if transferObjects is used on GasCoin | ||
fn is_gascoin_transfer(tx: SuiTransactionBlockKind) -> bool { | ||
match tx { | ||
SuiTransactionBlockKind::ProgrammableTransaction(pt) => { | ||
let SuiProgrammableTransactionBlock { | ||
inputs: _, | ||
commands, | ||
} = &pt; | ||
return commands.iter().any(|command| match command { | ||
SuiCommand::TransferObjects(objs, _) => { | ||
objs.iter().any(|&obj| obj == SuiArgument::GasCoin) | ||
} | ||
_ => false, | ||
}); | ||
} | ||
_ => {} | ||
} | ||
false | ||
} | ||
|
||
/// If GasCoin is transferred as a part of transferObjects, operations need to be | ||
/// updated such that: | ||
/// 1) gas owner needs to be assigned back to the previous owner | ||
/// 2) SuiBalanceChange type needs to be converted to PaySui for | ||
/// previous and new gas owners and their balances need to be adjusted for the gas | ||
fn process_gascoin_transfer( | ||
coin_change_operations: &mut impl Iterator<Item = crate::operations::Operation>, | ||
tx: SuiTransactionBlockKind, | ||
prev_gas_owner: SuiAddress, | ||
new_gas_owner: SuiAddress, | ||
gas_used: i128, | ||
) -> Result<Vec<Operation>, anyhow::Error> { | ||
let mut gascoin_transfer_operations = vec![]; | ||
if Self::is_gascoin_transfer(tx) { | ||
for operation in coin_change_operations.into_iter() { | ||
match operation.type_ { | ||
OperationType::Gas => { | ||
// change gas account back to the previous owner as it is the one | ||
// who paid for the txn (this is the format Rosetta wants to process) | ||
gascoin_transfer_operations.push(Operation::gas(prev_gas_owner, gas_used)) | ||
} | ||
OperationType::SuiBalanceChange => { | ||
let account = operation | ||
.account | ||
.ok_or_else(|| anyhow!("Missing account for a balance-change"))?; | ||
let mut amount = operation | ||
.amount | ||
.ok_or_else(|| anyhow!("Missing amount for a balance-change"))?; | ||
let mut is_convert_to_pay_sui = false; | ||
if account.address == prev_gas_owner && amount.currency == *SUI { | ||
// previous owner's balance needs to be adjusted for gas | ||
amount.value -= gas_used; | ||
is_convert_to_pay_sui = true; | ||
} else if account.address == new_gas_owner && amount.currency == *SUI { | ||
// new owner's balance needs to be adjusted for gas | ||
amount.value += gas_used; | ||
is_convert_to_pay_sui = true; | ||
} | ||
if is_convert_to_pay_sui { | ||
gascoin_transfer_operations.push(Operation::pay_sui( | ||
operation.status, | ||
account.address, | ||
amount.value, | ||
)); | ||
} else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should not edit every balance-change. Only the gas-coin sender and the recipient ones. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And we should also make sure that these addition and subtraction each happen only once. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
gascoin_transfer_operations.push(Operation::balance_change( | ||
operation.status, | ||
account.address, | ||
amount.value, | ||
amount.currency, | ||
)); | ||
} | ||
} | ||
_ => { | ||
return Err(anyhow!( | ||
"Discarding unsupported operation type {:?}", | ||
operation.type_ | ||
)) | ||
} | ||
} | ||
} | ||
} | ||
Ok(gascoin_transfer_operations) | ||
} | ||
} | ||
|
||
impl Operations { | ||
|
@@ -592,7 +677,7 @@ impl Operations { | |
- gas_summary.computation_cost as i128; | ||
|
||
let status = Some(effect.into_status().into()); | ||
let ops = Operations::try_from_data(tx.data, status)?; | ||
let ops = Operations::try_from_data(tx.data.clone(), status)?; | ||
let ops = ops.into_iter(); | ||
|
||
// We will need to subtract the operation amounts from the actual balance | ||
|
@@ -662,17 +747,28 @@ impl Operations { | |
} | ||
|
||
// Extract coin change operations from balance changes | ||
let coin_change_operations = Self::process_balance_change( | ||
let mut coin_change_operations = Self::process_balance_change( | ||
gas_owner, | ||
gas_used, | ||
balance_changes, | ||
status, | ||
accounted_balances, | ||
accounted_balances.clone(), | ||
); | ||
|
||
// Take {gas, previous gas owner, new gas owner} out of coin_change_operations | ||
// and convert BalanceChange to PaySui when GasCoin is transferred | ||
let gascoin_transfer_operations = Self::process_gascoin_transfer( | ||
&mut coin_change_operations, | ||
tx.data.transaction().clone(), | ||
tx.data.gas_data().owner, | ||
gas_owner, | ||
gas_used, | ||
)?; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Every transaction that happens on-chain will pass from here. We need to be extra careful that we do not mess up the balance-changes. Does it make sense to add a sanity check that operations balance changes match the tx balance changes after editing the operations with gas-coins transfer? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think a sanity check is required. First, the |
||
let ops: Operations = ops | ||
.into_iter() | ||
.chain(coin_change_operations) | ||
.chain(gascoin_transfer_operations) | ||
.chain(staking_balance) | ||
.collect(); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if multiple balance changes exist there for sender or recipient? I think this block might double-count gas.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
balance_changes are derived from
effects
field and even though there were multiple transfers for sender/recipient, there is one entry per address so I think there is no risk of double-count here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could there be the case though, that the sender also receives an amount equal to the gas-coin transferred, so that there is no balance-change for them? I know that this is far-fetched, but every SUI tx passes through this parsing. In that case this operation mapping would fail, correct? Lastly, the
PaySui
operations need to have their amounts match. See also above comment about explicit handling versus mapping.Edit: Also even if the amount is not equal, it would introduce a discrepancy between the
PaySui
for receiver and thePaySui
for sender operations.Example ptb: