@radixdlt/atom-transaction-mapping
Version:
346 lines (298 loc) • 11.1 kB
text/typescript
/* eslint-disable */
import { TokensActionBase } from '@radixdlt/actions'
import { AddressT } from '@radixdlt/account'
import {
AnyUpParticle,
FixedSupplyTokenDefinitionParticle,
MutableSupplyTokenDefinitionParticle,
ResourceIdentifier,
Spin,
TokenDefinitionParticleBase,
TokenDefinitionParticleInput,
tokenOwnerOnly,
TokenParticle,
tokenPermissionsAll,
TransferrableTokensParticle,
TransferrableTokensParticleT,
upParticle,
UpParticle,
} from '@radixdlt/atom'
import { Amount, maxAmount } from '@radixdlt/primitives'
import { UInt256 } from '@radixdlt/uint256'
import { toAddress } from '../../atom/test/helpers/utility'
import { two, three, one, four, five } from '@radixdlt/primitives/src/amount'
import { ActionToParticleGroupsMapper } from '../src/toAtom/_types'
export type TestCaseReturn = ReturnType<typeof it>
export type TestVector<T extends TokenDefinitionParticleBase> = Readonly<{
mapper: ActionToParticleGroupsMapper
makeAction: (amount: number, actor?: AddressT) => TokensActionBase<unknown>
tokenDefinitionParticle: T
}>
export type TestCase<T extends TokenDefinitionParticleBase> = (
testVector: TestVector<T>,
) => TestCaseReturn
export const alice = toAddress(
'9S8khLHZa6FsyGo634xQo9QwLgSHGpXHHW764D5mPYBcrnfZV6RT',
)
export const bob = toAddress(
'9S9LHeQNFpNJYqLtTJeAbos1LCC5Q7HBiGwPf2oju3NRq5MBKAGt',
)
const carol = toAddress('9S8sKfN3wGyJdfyu9RwWvGKtZqq3R1NaxwT63VXi5dEZ6dUJXLyR')
const dan = toAddress('9SBFdPAkvquf9XX82D2Z9DzL2WdmNQGcrxFUnKpVytpkMjZWD9Rb')
const symbol = 'FOOBAR'
export const rri = ResourceIdentifier.fromAddressAndName({
address: alice,
name: symbol,
})
const name = 'Foobar Coin'
const tokenDefInput = <TokenDefinitionParticleInput>{
symbol,
name,
address: alice,
granularity: one,
}
export const fixedSupTokDefParticle = FixedSupplyTokenDefinitionParticle.create(
{
...tokenDefInput,
supply: maxAmount,
},
)._unsafeUnwrap()
export const mutableSupplyTokenDefinitionParticleAllCanMutate = MutableSupplyTokenDefinitionParticle.create(
{
...tokenDefInput,
permissions: tokenPermissionsAll.permissions,
},
)._unsafeUnwrap()
export const mutableSupplyTokenDefinitionParticleOnlyAliceCanMutate = MutableSupplyTokenDefinitionParticle.create(
{
...tokenDefInput,
permissions: tokenOwnerOnly.permissions,
},
)._unsafeUnwrap()
export const upTTP = (
amount: number,
tokenDefinitionParticle: TokenDefinitionParticleBase,
owner?: AddressT,
): UpParticle<TransferrableTokensParticleT> => {
return upParticle(
TransferrableTokensParticle.create({
granularity: tokenDefinitionParticle.granularity,
resourceIdentifier: tokenDefinitionParticle.resourceIdentifier,
address: owner ?? alice,
amount: Amount.inSmallestDenomination(UInt256.valueOf(amount)),
})._unsafeUnwrap(),
)
}
export const testMapperReturns___Unknown_Token___error_when_no_token_definition_particle = <
T extends TokenDefinitionParticleBase
>(
testVector: TestVector<T>,
): TestCaseReturn =>
it(`should fail with error 'Unknown Token' when no TokenDefinitionParticle at all present`, () => {
const mapper = testVector.mapper
const action = testVector.makeAction(4)
expect(mapper.actionType).toBe(action.actionType)
const particleGroupsResult = mapper.particleGroupsFromAction({
action: action,
upParticles: [], // EMPTY PARTICLES
addressOfActiveAccount: alice,
})
particleGroupsResult.match(
() => {
throw Error('expected error, but got none')
},
(f) =>
expect(f.message).toBe(
`Unknown token with identifier: '${action.resourceIdentifier.toString()}'`,
),
)
})
export const testMapperReturns___Insufficient_Balance___error_when_no_transferrable_tokens_particles = <
T extends TokenDefinitionParticleBase
>(
testVector: TestVector<T>,
): TestCaseReturn =>
it(`should fail with error 'Insufficient Balance' when ${testVector.tokenDefinitionParticle.radixParticleType} but no TransferrableTokensParticle(s) present.`, () => {
const mapper = testVector.mapper
const action = testVector.makeAction(4)
expect(mapper.actionType).toBe(action.actionType)
const spunUpParticles: AnyUpParticle[] = [
upParticle(testVector.tokenDefinitionParticle).eraseToAnyUp(),
// NO TransferrableTokensParticles to spend
]
const particleGroupsResult = mapper.particleGroupsFromAction({
action: action,
upParticles: spunUpParticles,
addressOfActiveAccount: alice,
})
particleGroupsResult.match(
() => {
throw Error('expected error, but got none')
},
(f) => expect(f.message).toBe(`Insufficient balance.`),
)
})
export const testMapperReturns___Insufficient_Balance___error_when_not_enough_transferrable_tokens_particles = <
T extends TokenDefinitionParticleBase
>(
testVector: TestVector<T>,
): void =>
it(`should fail with error 'Insufficient Balance' when not enough TransferrableTokensParticles present.`, () => {
const mapper = testVector.mapper
const action = testVector.makeAction(4)
expect(mapper.actionType).toBe(action.actionType)
const spunUpParticles: AnyUpParticle[] = [
upParticle(testVector.tokenDefinitionParticle),
upTTP(3, testVector.tokenDefinitionParticle), // 3 is less than 4.
].map((p) => p.eraseToAnyUp())
const particleGroupsResult = mapper.particleGroupsFromAction({
action: action,
upParticles: spunUpParticles,
addressOfActiveAccount: alice,
})
particleGroupsResult.match(
() => {
throw Error('expected error, but got none')
},
(f) => expect(f.message).toBe(`Insufficient balance.`),
)
})
export const testMapperReturns___Wrong_Sender___error_when_addressOfActiveAccount_is_someone_elses = <
T extends TokenDefinitionParticleBase
>(
testVector: TestVector<T>,
): void =>
it(`should fail with error 'Wrong sender/signer' when addressOfActiveAcount is someone elses.`, () => {
const mapper = testVector.mapper
const action = testVector.makeAction(5)
expect(mapper.actionType).toBe(action.actionType)
expect(alice.equals(carol)).toBe(false)
const spunUpParticles: AnyUpParticle[] = [
upParticle(testVector.tokenDefinitionParticle),
upTTP(2, testVector.tokenDefinitionParticle),
upTTP(3, testVector.tokenDefinitionParticle),
].map((p) => p.eraseToAnyUp())
const particleGroupsResult = mapper.particleGroupsFromAction({
action: action,
upParticles: spunUpParticles,
addressOfActiveAccount: carol, // <-- WRONG!
})
particleGroupsResult.match(
() => {
throw Error('expected error, but got none')
},
(f) => expect(f.message).toBe(`Wrong sender/signer`),
)
})
export const testMapperReturns___Insufficient_Balance___error_when_some_of_transferrable_tokens_particles_belongs_to_someone_else = <
T extends TokenDefinitionParticleBase
>(
testVector: TestVector<T>,
): TestCaseReturn =>
it(`should fail with error 'Insufficient Balance' when some of the TransferrableTokensParticles belong to someone else.`, () => {
const mapper = testVector.mapper
const action = testVector.makeAction(4)
expect(mapper.actionType).toBe(action.actionType)
expect(alice.equals(carol)).toBe(false)
const spunUpParticles: AnyUpParticle[] = [
upParticle(testVector.tokenDefinitionParticle),
upTTP(2, testVector.tokenDefinitionParticle, dan), // belongs to Dan instead of Alkice.
upTTP(3, testVector.tokenDefinitionParticle),
].map((p) => p.eraseToAnyUp())
const particleGroupsResult = mapper.particleGroupsFromAction({
action: action,
upParticles: spunUpParticles,
addressOfActiveAccount: alice,
})
particleGroupsResult.match(
() => {
throw Error('expected error, but got none')
},
(f) => expect(f.message).toBe(`Insufficient balance.`),
)
})
export const testMapperReturns___works_with_change = <
T extends TokenDefinitionParticleBase
>(
validateMigratedParticle: (migratedParticle: TokenParticle) => void,
validateOutputParticle: (outputParticle: TokenParticle) => void,
testVector: TestVector<T>,
): TestCaseReturn =>
it(`should work with a ${testVector.tokenDefinitionParticle.radixParticleType} and some TransferrableTokensParticles with change back.`, () => {
const mapper = testVector.mapper
const action = testVector.makeAction(4)
expect(mapper.actionType).toBe(action.actionType)
const spunUpParticles: AnyUpParticle[] = [
upParticle(testVector.tokenDefinitionParticle),
upTTP(2, testVector.tokenDefinitionParticle),
upTTP(3, testVector.tokenDefinitionParticle),
].map((p) => p.eraseToAnyUp())
const particleGroups = mapper
.particleGroupsFromAction({
action: action,
upParticles: spunUpParticles,
addressOfActiveAccount: alice,
})
._unsafeUnwrap()
expect(particleGroups.length).toBe(1)
const spunParticles = particleGroups[0].spunParticles.spunParticles
expect(spunParticles.length).toBe(4)
const sp0 = spunParticles[0]
expect(sp0.spin).toBe(Spin.DOWN)
const p0 = sp0.particle as TransferrableTokensParticleT
expect(p0.amount.equals(two)).toBe(true)
const sp1 = spunParticles[1]
expect(sp1.spin).toBe(Spin.DOWN)
const p1 = sp1.particle as TransferrableTokensParticleT
expect(p1.amount.equals(three)).toBe(true)
// Change back to Alice
const sp2 = spunParticles[2]
expect(sp2.spin).toBe(Spin.UP)
const p2 = sp2.particle as TokenParticle
expect(p2.amount.equals(one)).toBe(true)
validateMigratedParticle(p2)
const sp3 = spunParticles[3]
expect(sp3.spin).toBe(Spin.UP)
const p3 = sp3.particle as TokenParticle
expect(p3.amount.equals(four)).toBe(true)
validateOutputParticle(p3)
})
export const testMapperReturns___works_without_change = <
T extends TokenDefinitionParticleBase
>(
validateOutputParticle: (outputParticle: TokenParticle) => void,
testVector: TestVector<T>,
): TestCaseReturn =>
it(`should work with a ${testVector.tokenDefinitionParticle.radixParticleType} and some TransferrableTokensParticles with no change back.`, () => {
const mapper = testVector.mapper
const action = testVector.makeAction(5)
expect(mapper.actionType).toBe(action.actionType)
const spunUpParticles: AnyUpParticle[] = [
upParticle(testVector.tokenDefinitionParticle),
upTTP(2, testVector.tokenDefinitionParticle),
upTTP(3, testVector.tokenDefinitionParticle),
].map((p) => p.eraseToAnyUp())
const particleGroups = mapper
.particleGroupsFromAction({
action: action,
upParticles: spunUpParticles,
addressOfActiveAccount: alice,
})
._unsafeUnwrap()
expect(particleGroups.length).toBe(1)
const spunParticles = particleGroups[0].spunParticles.spunParticles
expect(spunParticles.length).toBe(3)
const sp0 = spunParticles[0]
expect(sp0.spin).toBe(Spin.DOWN)
const p0 = sp0.particle as TransferrableTokensParticleT
expect(p0.amount.equals(two)).toBe(true)
const sp1 = spunParticles[1]
expect(sp1.spin).toBe(Spin.DOWN)
const p1 = sp1.particle as TransferrableTokensParticleT
expect(p1.amount.equals(three)).toBe(true)
const sp2 = spunParticles[2]
expect(sp2.spin).toBe(Spin.UP)
const p2 = sp2.particle as TokenParticle
expect(p2.amount.equals(five)).toBe(true)
validateOutputParticle(p2)
})