Skip to content

Commit

Permalink
Bug transacting with capacity should not require tokens to cover tran…
Browse files Browse the repository at this point in the history
…saction cost (#1851)

# Goal
The goal of this PR is to fix a frequency-tx-payment bug to allow
capacity holders to transact when their free balance is less than the
minimum balance, or Existential Deposit (ED).

Closes #1850

# Discussion

- [x] `withdraw_fee` check updated to use `total_balance` instead of
`balance`, which ensures that if an account has capacity (`frozen` > 0)
but has no available balance (`free == frozen`) transactions are still
successful
- [x] Ensures that additional provider keys that have no ED cannot
transact successfully.
- [x] Add e2e-test that successfully claims a handle with 0 available
balance

# How to Test
- Ensure that e2e-tests complete successfully

# Checklist
- [x] Chain spec updated
- [ ] Custom RPC OR Runtime API added/changed? Updated js/api-augment.
- [ ] Design doc(s) updated
- [x] Tests added
- [ ] Benchmarks added
- [ ] Weights updated

---------

Co-authored-by: Matthew Orris <--help>
Co-authored-by: Joe Caputo <joseph.caputo@unfinished.com>
  • Loading branch information
mattheworris and JoeCap08055 committed Jan 24, 2024
1 parent 8679b88 commit 9bc974e
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 3 deletions.
45 changes: 45 additions & 0 deletions e2e/capacity/transactions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,51 @@ describe('Capacity Transactions', function () {
});
});

describe('when capacity eligible transaction and balance less than ED', function () {
let capacityKeys: KeyringPair;
let capacityProvider: u64;

before(async function () {
capacityKeys = createKeys('CapacityKeys');
capacityProvider = await createMsaAndProvider(fundingSource, capacityKeys, 'CapacityProvider', FUNDS_AMOUNT);
});

it('successfully pays with Capacity for eligible transaction - claimHandle [balance < ED]', async function () {
await assert.doesNotReject(stakeToProvider(fundingSource, capacityKeys, capacityProvider, amountStaked));
// Empty the account to ensure the balance is less than ED
await ExtrinsicHelper.emptyAccount(capacityKeys, fundingSource.address).signAndSend();
// Confirm that the available balance is less than ED
// The available balance is the free balance minus the frozen balance
const capacityAcctInfo = await ExtrinsicHelper.getAccountInfo(capacityKeys.address);
assert.equal(capacityAcctInfo.data.frozen.toBigInt(), amountStaked);
assert.equal(capacityAcctInfo.data.free.toBigInt(), amountStaked);

// Confirm that a transfer fails because the available balance is 0
const failTransferObj = ExtrinsicHelper.transferFunds(capacityKeys, fundingSource, 1n * CENTS);
assert.rejects(failTransferObj.signAndSend('current'), {
name: 'RpcError',
message: '1010: Invalid Transaction: Inability to pay some fees , e.g. account balance too low',
});

const handle = getTestHandle();
const expiration = (await getBlockNumber()) + 10;
const handle_vec = new Bytes(ExtrinsicHelper.api.registry, handle);
const handlePayload = {
baseHandle: handle_vec,
expiration: expiration,
};
const claimHandlePayload = ExtrinsicHelper.api.registry.createType(
'CommonPrimitivesHandlesClaimHandlePayload',
handlePayload
);
const claimHandle = ExtrinsicHelper.claimHandle(capacityKeys, claimHandlePayload);
const { eventMap } = await claimHandle.payWithCapacity();
assertEvent(eventMap, 'system.ExtrinsicSuccess');
assertEvent(eventMap, 'capacity.CapacityWithdrawn');
assertEvent(eventMap, 'handles.HandleClaimed');
});
});

// When a user attempts to pay for a non-capacity transaction with Capacity,
// it should error and drop the transaction from the transaction-pool.
it('fails to pay with Capacity for a non-capacity transaction', async function () {
Expand Down
2 changes: 1 addition & 1 deletion pallets/frequency-tx-payment/src/payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ where
fee: Self::Balance,
) -> Result<Self::Balance, TransactionValidityError> {
ensure!(
Curr::balance(key) >= Curr::minimum_balance(),
Curr::total_balance(key) >= Curr::minimum_balance(),
TransactionValidityError::Invalid(InvalidTransaction::Payment)
);

Expand Down
4 changes: 2 additions & 2 deletions runtime/frequency/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("frequency"),
impl_name: create_runtime_str!("frequency"),
authoring_version: 1,
spec_version: 70,
spec_version: 71,
impl_version: 0,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
Expand All @@ -344,7 +344,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("frequency-rococo"),
impl_name: create_runtime_str!("frequency"),
authoring_version: 1,
spec_version: 70,
spec_version: 71,
impl_version: 0,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
Expand Down

0 comments on commit 9bc974e

Please sign in to comment.