-
Notifications
You must be signed in to change notification settings - Fork 525
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add Clawback amendment support (#2353)
* Add Clawback transaction * Account flag lsfAllowTrustLineClawback * Support bitwise flag checking of 64 bit flags
- Loading branch information
1 parent
c564161
commit 63ea6e8
Showing
14 changed files
with
284 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { ValidationError } from '../../errors' | ||
import { IssuedCurrencyAmount } from '../common' | ||
|
||
import { | ||
BaseTransaction, | ||
validateBaseTransaction, | ||
isIssuedCurrency, | ||
} from './common' | ||
|
||
/** | ||
* The Clawback transaction is used by the token issuer to claw back | ||
* issued tokens from a holder. | ||
*/ | ||
export interface Clawback extends BaseTransaction { | ||
TransactionType: 'Clawback' | ||
/** | ||
* Indicates the AccountID that submitted this transaction. The account MUST | ||
* be the issuer of the currency. | ||
*/ | ||
Account: string | ||
/** | ||
* The amount of currency to deliver, and it must be non-XRP. The nested field | ||
* names MUST be lower-case. The `issuer` field MUST be the holder's address, | ||
* whom to be clawed back. | ||
*/ | ||
Amount: IssuedCurrencyAmount | ||
} | ||
|
||
/** | ||
* Verify the form and type of an Clawback at runtime. | ||
* | ||
* @param tx - An Clawback Transaction. | ||
* @throws When the Clawback is Malformed. | ||
*/ | ||
export function validateClawback(tx: Record<string, unknown>): void { | ||
validateBaseTransaction(tx) | ||
|
||
if (tx.Amount == null) { | ||
throw new ValidationError('Clawback: missing field Amount') | ||
} | ||
|
||
if (!isIssuedCurrency(tx.Amount)) { | ||
throw new ValidationError('Clawback: invalid Amount') | ||
} | ||
|
||
if (isIssuedCurrency(tx.Amount) && tx.Account === tx.Amount.issuer) { | ||
throw new ValidationError('Clawback: invalid holder Account') | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
115 changes: 115 additions & 0 deletions
115
packages/xrpl/test/integration/transactions/clawback.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import { assert } from 'chai' | ||
|
||
import { | ||
AccountSet, | ||
AccountSetAsfFlags, | ||
TrustSet, | ||
Payment, | ||
Clawback, | ||
} from '../../../src' | ||
import serverUrl from '../serverUrl' | ||
import { | ||
setupClient, | ||
teardownClient, | ||
type XrplIntegrationTestContext, | ||
} from '../setup' | ||
import { generateFundedWallet, testTransaction } from '../utils' | ||
|
||
// how long before each test case times out | ||
const TIMEOUT = 20000 | ||
|
||
describe('Clawback', function () { | ||
let testContext: XrplIntegrationTestContext | ||
|
||
beforeEach(async () => { | ||
testContext = await setupClient(serverUrl) | ||
}) | ||
afterEach(async () => teardownClient(testContext)) | ||
|
||
it( | ||
'base', | ||
async () => { | ||
const wallet2 = await generateFundedWallet(testContext.client) | ||
|
||
const setupAccountSetTx: AccountSet = { | ||
TransactionType: 'AccountSet', | ||
Account: testContext.wallet.classicAddress, | ||
SetFlag: AccountSetAsfFlags.asfAllowTrustLineClawback, | ||
} | ||
await testTransaction( | ||
testContext.client, | ||
setupAccountSetTx, | ||
testContext.wallet, | ||
) | ||
|
||
const setupTrustSetTx: TrustSet = { | ||
TransactionType: 'TrustSet', | ||
Account: wallet2.classicAddress, | ||
LimitAmount: { | ||
currency: 'USD', | ||
issuer: testContext.wallet.classicAddress, | ||
value: '1000', | ||
}, | ||
} | ||
await testTransaction(testContext.client, setupTrustSetTx, wallet2) | ||
|
||
const setupPaymentTx: Payment = { | ||
TransactionType: 'Payment', | ||
Account: testContext.wallet.classicAddress, | ||
Destination: wallet2.classicAddress, | ||
Amount: { | ||
currency: 'USD', | ||
issuer: testContext.wallet.classicAddress, | ||
value: '1000', | ||
}, | ||
} | ||
await testTransaction( | ||
testContext.client, | ||
setupPaymentTx, | ||
testContext.wallet, | ||
) | ||
|
||
// verify that line is created | ||
const objectsResponse = await testContext.client.request({ | ||
command: 'account_objects', | ||
account: wallet2.classicAddress, | ||
type: 'state', | ||
}) | ||
assert.lengthOf( | ||
objectsResponse.result.account_objects, | ||
1, | ||
'Should be exactly one line on the ledger', | ||
) | ||
|
||
// actual test - clawback | ||
const tx: Clawback = { | ||
TransactionType: 'Clawback', | ||
Account: testContext.wallet.classicAddress, | ||
Amount: { | ||
currency: 'USD', | ||
issuer: wallet2.classicAddress, | ||
value: '500', | ||
}, | ||
} | ||
await testTransaction(testContext.client, tx, testContext.wallet) | ||
|
||
// verify amount clawed back | ||
const linesResponse = await testContext.client.request({ | ||
command: 'account_lines', | ||
account: wallet2.classicAddress, | ||
}) | ||
|
||
assert.lengthOf( | ||
linesResponse.result.lines, | ||
1, | ||
'Should be exactly one line on the ledger', | ||
) | ||
assert.equal( | ||
'500', | ||
linesResponse.result.lines[0].balance, | ||
`Holder balance incorrect after Clawback`, | ||
) | ||
}, | ||
TIMEOUT, | ||
) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import { assert } from 'chai' | ||
|
||
import { validate, ValidationError } from '../../src' | ||
|
||
/** | ||
* Clawback Transaction Verification Testing. | ||
* | ||
* Providing runtime verification testing for each specific transaction type. | ||
*/ | ||
describe('Clawback', function () { | ||
it(`verifies valid Clawback`, function () { | ||
const validClawback = { | ||
TransactionType: 'Clawback', | ||
Amount: { | ||
currency: 'DSH', | ||
issuer: 'rcXY84C4g14iFp6taFXjjQGVeHqSCh9RX', | ||
value: '43.11584856965009', | ||
}, | ||
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm', | ||
} as any | ||
|
||
assert.doesNotThrow(() => validate(validClawback)) | ||
}) | ||
|
||
it(`throws w/ missing Amount`, function () { | ||
const missingAmount = { | ||
TransactionType: 'Clawback', | ||
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm', | ||
} as any | ||
|
||
assert.throws( | ||
() => validate(missingAmount), | ||
ValidationError, | ||
'Clawback: missing field Amount', | ||
) | ||
}) | ||
|
||
it(`throws w/ invalid Amount`, function () { | ||
const invalidAmount = { | ||
TransactionType: 'Clawback', | ||
Amount: 100000000, | ||
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm', | ||
} as any | ||
|
||
assert.throws( | ||
() => validate(invalidAmount), | ||
ValidationError, | ||
'Clawback: invalid Amount', | ||
) | ||
|
||
const invalidStrAmount = { | ||
TransactionType: 'Clawback', | ||
Amount: '1234', | ||
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm', | ||
} as any | ||
|
||
assert.throws( | ||
() => validate(invalidStrAmount), | ||
ValidationError, | ||
'Clawback: invalid Amount', | ||
) | ||
}) | ||
|
||
it(`throws w/ invalid holder Account`, function () { | ||
const invalidAccount = { | ||
TransactionType: 'Clawback', | ||
Amount: { | ||
currency: 'DSH', | ||
issuer: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm', | ||
value: '43.11584856965009', | ||
}, | ||
Account: 'rWYkbWkCeg8dP6rXALnjgZSjjLyih5NXm', | ||
} as any | ||
|
||
assert.throws( | ||
() => validate(invalidAccount), | ||
ValidationError, | ||
'Clawback: invalid holder Account', | ||
) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters