Skip to content

Commit 7fc0f2c

Browse files
committed
refactor: improve post condition test with cleaner assertions
1 parent 4e36e26 commit 7fc0f2c

File tree

1 file changed

+293
-0
lines changed

1 file changed

+293
-0
lines changed

packages/transactions/tests/builder.test.ts

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
BadNonceRejection,
1919
CONTRACT_ABI_PATH,
2020
ClarityAbi,
21+
Pc,
2122
READONLY_FUNCTION_CALL_PATH,
2223
StacksWireType,
2324
TRANSACTION_FEE_ESTIMATE_PATH,
@@ -83,8 +84,11 @@ import {
8384
AddressHashMode,
8485
AuthType,
8586
ClarityVersion,
87+
FungibleConditionCode,
8688
PayloadType,
8789
PostConditionMode,
90+
PostConditionPrincipalId,
91+
PostConditionType,
8892
PubKeyEncoding,
8993
TxRejectedReason,
9094
} from '../src/constants';
@@ -98,6 +102,7 @@ import {
98102
transactionToHex,
99103
} from '../src/transaction';
100104
import { cloneDeep, randomBytes } from '../src/utils';
105+
import { FungiblePostConditionWire, STXPostConditionWire } from '../src/wire/types';
101106

102107
function setSignature(
103108
unsignedTransaction: StacksTransactionWire,
@@ -1951,6 +1956,294 @@ test('Transaction broadcast fails', async () => {
19511956
await expect(broadcastTransaction({ transaction })).rejects.toThrow();
19521957
});
19531958

1959+
test('Make contract-call with serialized hex post conditions', async () => {
1960+
const contractAddress = 'ST3KC0MTNW34S1ZXD36JYKFD3JJMWA01M55DSJ4JE';
1961+
const contractName = 'kv-store';
1962+
const functionName = 'get-value';
1963+
const buffer = bufferCV(utf8ToBytes('foo'));
1964+
const senderKey = 'e494f188c2d35887531ba474c433b1e41fadd8eb824aca983447fd4bb8b277a801';
1965+
const fee = 0;
1966+
1967+
// These are serialized post conditions
1968+
const stxPostConditionHex = '00021a5dd8ff3545259925b982524807686567eec2933f03000000000000000a'; // STX post condition for ST1EXHZSN8MJSJ9DSG994G1V8CNKYXGMK7Z4SA6DH, gte, 10
1969+
const ftPostConditionHex =
1970+
'01021a5dd8ff3545259925b982524807686567eec2933f1ac989ba53bbb27a76ef5e8499e65f69c7798fd5d113746573742d61737365742d636f6e74726163740f746573742d61737365742d6e616d650400000000000003e8'; // FT post condition
1971+
1972+
const postConditions = [stxPostConditionHex, ftPostConditionHex];
1973+
1974+
const transaction = await makeContractCall({
1975+
contractAddress,
1976+
contractName,
1977+
functionName,
1978+
functionArgs: [buffer],
1979+
senderKey,
1980+
fee,
1981+
nonce: 1,
1982+
network: STACKS_TESTNET,
1983+
postConditions,
1984+
postConditionMode: 'deny',
1985+
});
1986+
1987+
expect(() => transaction.verifyOrigin()).not.toThrow();
1988+
1989+
// Re-serialize and check if the serialized post conditions were correctly handled
1990+
const serializedTx = transaction.serialize();
1991+
const bytesReader = new BytesReader(hexToBytes(serializedTx));
1992+
const deserializedTx = deserializeTransaction(bytesReader);
1993+
1994+
// Should have 2 post conditions
1995+
expect(deserializedTx.postConditions.values.length).toBe(2);
1996+
1997+
const firstPC = deserializedTx.postConditions.values[0];
1998+
expect(firstPC.conditionType).toBe(PostConditionType.STX);
1999+
expect((firstPC as STXPostConditionWire).amount.toString()).toBe('10');
2000+
2001+
const secondPC = deserializedTx.postConditions.values[1];
2002+
expect(secondPC.conditionType).toBe(PostConditionType.Fungible);
2003+
expect((secondPC as FungiblePostConditionWire).amount.toString()).toBe('1000');
2004+
});
2005+
2006+
test('Make contract deploy with mixed post conditions (objects and serialized hex)', async () => {
2007+
const contractName = 'kv-store';
2008+
const codeBody = fs.readFileSync('./tests/contracts/kv-store.clar').toString();
2009+
const senderKey = 'e494f188c2d35887531ba474c433b1e41fadd8eb824aca983447fd4bb8b277a801';
2010+
const fee = 0;
2011+
const nonce = 0;
2012+
2013+
// Object post condition
2014+
const stxPostCondition: StxPostCondition = {
2015+
type: 'stx-postcondition',
2016+
address: 'ST1EXHZSN8MJSJ9DSG994G1V8CNKYXGMK7Z4SA6DH',
2017+
condition: 'eq',
2018+
amount: 5000,
2019+
};
2020+
2021+
// Serialized hex post condition
2022+
const serializedPostConditionHex =
2023+
'00021a5dd8ff3545259925b982524807686567eec2933f03000000000000000a'; // STX post condition for the same address, gte, 10
2024+
2025+
const transaction = await makeContractDeploy({
2026+
contractName,
2027+
codeBody,
2028+
senderKey,
2029+
fee,
2030+
nonce,
2031+
network: STACKS_TESTNET,
2032+
postConditions: [stxPostCondition, serializedPostConditionHex],
2033+
postConditionMode: 'deny',
2034+
});
2035+
2036+
expect(() => transaction.verifyOrigin()).not.toThrow();
2037+
2038+
// Re-serialize and check if both post conditions were correctly handled
2039+
const serializedTx = transaction.serialize();
2040+
const bytesReader = new BytesReader(hexToBytes(serializedTx));
2041+
const deserializedTx = deserializeTransaction(bytesReader);
2042+
2043+
// Should have 2 post conditions
2044+
expect(deserializedTx.postConditions.values.length).toBe(2);
2045+
2046+
const firstPC = deserializedTx.postConditions.values[0];
2047+
expect(firstPC.conditionType).toBe(PostConditionType.STX);
2048+
expect((firstPC as STXPostConditionWire).amount).toBe(5000n);
2049+
2050+
const secondPC = deserializedTx.postConditions.values[1];
2051+
expect(secondPC.conditionType).toBe(PostConditionType.STX);
2052+
expect((secondPC as STXPostConditionWire).amount).toBe(10n);
2053+
});
2054+
2055+
test('Comprehensive post condition test for contract call and deploy', async () => {
2056+
// Common test variables
2057+
const senderKey = 'e494f188c2d35887531ba474c433b1e41fadd8eb824aca983447fd4bb8b277a801';
2058+
const fee = 0;
2059+
const nonce = 0;
2060+
2061+
// Test data - Post Conditions in different formats
2062+
2063+
// 1. Normal post condition objects
2064+
const stxPostCondition: StxPostCondition = {
2065+
type: 'stx-postcondition',
2066+
address: 'ST1EXHZSN8MJSJ9DSG994G1V8CNKYXGMK7Z4SA6DH',
2067+
condition: 'eq',
2068+
amount: 5000,
2069+
};
2070+
2071+
const ftPostCondition: FungiblePostCondition = {
2072+
type: 'ft-postcondition',
2073+
address: 'ST1EXHZSN8MJSJ9DSG994G1V8CNKYXGMK7Z4SA6DH',
2074+
condition: 'gte',
2075+
asset: 'ST34RKEJKQES7MXQFBT29KSJZD73QK3YNT5N56C6X.test-asset-contract::test-asset-name',
2076+
amount: 1000,
2077+
};
2078+
2079+
// 2. Wire format post conditions
2080+
const stxPostConditionWire: STXPostConditionWire = {
2081+
type: StacksWireType.PostCondition,
2082+
conditionType: PostConditionType.STX,
2083+
principal: {
2084+
type: StacksWireType.Principal,
2085+
prefix: PostConditionPrincipalId.Standard,
2086+
address: {
2087+
type: StacksWireType.Address,
2088+
version: 22,
2089+
hash160: '5dd8ff3545259925b982524807686567eec2933f',
2090+
},
2091+
},
2092+
conditionCode: FungibleConditionCode.Less,
2093+
amount: 7500n,
2094+
};
2095+
2096+
// 3. Hex serialized post conditions
2097+
const hexSerializedStxPostCondition =
2098+
'00021a5dd8ff3545259925b982524807686567eec2933f03000000000000000a'; // STX post condition for ST1EXHZSN8MJSJ9DSG994G1V8CNKYXGMK7Z4SA6DH, gte, 10
2099+
2100+
const hexSerializedFtPostCondition =
2101+
'01021a5dd8ff3545259925b982524807686567eec2933f1ac989ba53bbb27a76ef5e8499e65f69c7798fd5d113746573742d61737365742d636f6e74726163740f746573742d61737365742d6e616d650400000000000003e8'; // FT post condition
2102+
2103+
// Create an array with all post condition types
2104+
const allPostConditions = [
2105+
stxPostCondition,
2106+
ftPostCondition,
2107+
stxPostConditionWire,
2108+
hexSerializedStxPostCondition,
2109+
hexSerializedFtPostCondition,
2110+
];
2111+
2112+
// Define expected post condition values after deserialization for easier testing
2113+
const expectedPostConditions = [
2114+
{
2115+
conditionType: PostConditionType.STX,
2116+
amount: 5000n,
2117+
conditionCode: FungibleConditionCode.Equal,
2118+
},
2119+
{
2120+
conditionType: PostConditionType.Fungible,
2121+
amount: 1000n,
2122+
conditionCode: FungibleConditionCode.GreaterEqual,
2123+
},
2124+
{
2125+
conditionType: PostConditionType.STX,
2126+
amount: 7500n,
2127+
conditionCode: FungibleConditionCode.Less,
2128+
},
2129+
{
2130+
conditionType: PostConditionType.STX,
2131+
amount: 10n,
2132+
conditionCode: FungibleConditionCode.GreaterEqual,
2133+
},
2134+
{
2135+
conditionType: PostConditionType.Fungible,
2136+
amount: 1000n,
2137+
conditionCode: FungibleConditionCode.Less,
2138+
},
2139+
];
2140+
2141+
// Test Contract Deploy with all post condition types
2142+
const contractName = 'test-contract';
2143+
const codeBody = fs.readFileSync('./tests/contracts/kv-store.clar').toString();
2144+
2145+
const deployTx = await makeContractDeploy({
2146+
contractName,
2147+
codeBody,
2148+
senderKey,
2149+
fee,
2150+
nonce,
2151+
network: STACKS_TESTNET,
2152+
postConditions: allPostConditions,
2153+
postConditionMode: 'deny',
2154+
});
2155+
2156+
// Verify transaction and post conditions
2157+
expect(() => deployTx.verifyOrigin()).not.toThrow();
2158+
2159+
// Re-serialize and check if post conditions were correctly handled
2160+
let serializedTx = deployTx.serialize();
2161+
let deserializedTx = deserializeTransaction(serializedTx);
2162+
2163+
// Should have all 5 post conditions
2164+
expect(deserializedTx.postConditions.values.length).toBe(5);
2165+
2166+
// Verify all post conditions from deserialized transaction match expected values
2167+
const deployPCs = deserializedTx.postConditions.values;
2168+
2169+
// Validate all post conditions at once with a loop
2170+
expectedPostConditions.forEach((expected, index) => {
2171+
const actual = deployPCs[index];
2172+
expect(actual.conditionType).toBe(expected.conditionType);
2173+
2174+
if (actual.conditionType === PostConditionType.STX) {
2175+
expect((actual as STXPostConditionWire).amount).toBe(expected.amount);
2176+
expect((actual as STXPostConditionWire).conditionCode).toBe(expected.conditionCode);
2177+
} else if (actual.conditionType === PostConditionType.Fungible) {
2178+
expect((actual as FungiblePostConditionWire).amount).toBe(expected.amount);
2179+
expect((actual as FungiblePostConditionWire).conditionCode).toBe(expected.conditionCode);
2180+
}
2181+
});
2182+
2183+
// Test Contract Call with same post conditions
2184+
const contractAddress = 'ST3KC0MTNW34S1ZXD36JYKFD3JJMWA01M55DSJ4JE';
2185+
const contractCallName = 'kv-store';
2186+
const functionName = 'get-value';
2187+
const buffer = bufferCV(utf8ToBytes('test-key'));
2188+
2189+
const callTx = await makeContractCall({
2190+
contractAddress,
2191+
contractName: contractCallName,
2192+
functionName,
2193+
functionArgs: [buffer],
2194+
senderKey,
2195+
fee,
2196+
nonce,
2197+
network: STACKS_TESTNET,
2198+
postConditions: allPostConditions,
2199+
postConditionMode: 'deny',
2200+
});
2201+
2202+
// Verify transaction and post conditions
2203+
expect(() => callTx.verifyOrigin()).not.toThrow();
2204+
2205+
// Re-serialize and check if post conditions were correctly handled
2206+
serializedTx = callTx.serialize();
2207+
deserializedTx = deserializeTransaction(serializedTx);
2208+
2209+
// Should have all 5 post conditions
2210+
expect(deserializedTx.postConditions.values.length).toBe(5);
2211+
2212+
// Verify all post conditions from deserialized transaction match expected values
2213+
const callPCs = deserializedTx.postConditions.values;
2214+
2215+
// Validate all post conditions at once with a loop (same expected values)
2216+
expectedPostConditions.forEach((expected, index) => {
2217+
const actual = callPCs[index];
2218+
expect(actual.conditionType).toBe(expected.conditionType);
2219+
2220+
if (actual.conditionType === PostConditionType.STX) {
2221+
expect((actual as STXPostConditionWire).amount).toBe(expected.amount);
2222+
expect((actual as STXPostConditionWire).conditionCode).toBe(expected.conditionCode);
2223+
} else if (actual.conditionType === PostConditionType.Fungible) {
2224+
expect((actual as FungiblePostConditionWire).amount).toBe(expected.amount);
2225+
expect((actual as FungiblePostConditionWire).conditionCode).toBe(expected.conditionCode);
2226+
}
2227+
});
2228+
2229+
// Test conversion back to JSON format using Pc.fromHex
2230+
const expectedPcFromHex = {
2231+
type: 'stx-postcondition',
2232+
address: 'ST1EXHZSN8MJSJ9DSG994G1V8CNKYXGMK7Z4SA6DH',
2233+
condition: 'gte',
2234+
amount: '10',
2235+
};
2236+
2237+
// Check that Pc.fromHex correctly converts the serialized hex post condition
2238+
const pcFromHex = Pc.fromHex(hexSerializedStxPostCondition) as StxPostCondition;
2239+
expect({
2240+
type: pcFromHex.type,
2241+
address: pcFromHex.address,
2242+
condition: pcFromHex.condition,
2243+
amount: pcFromHex.amount,
2244+
}).toEqual(expectedPcFromHex);
2245+
});
2246+
19542247
test('Make contract-call with network ABI validation', async () => {
19552248
const contractAddress = 'ST3KC0MTNW34S1ZXD36JYKFD3JJMWA01M55DSJ4JE';
19562249
const contractName = 'kv-store';

0 commit comments

Comments
 (0)