Skip to content

Commit b029c84

Browse files
authored
fix: tx-link decode (#762)
1 parent a2c02a6 commit b029c84

File tree

3 files changed

+256
-26
lines changed

3 files changed

+256
-26
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Gno URL Parameters
2+
export const GNO_HELP_MARKER = '$help';
3+
export const GNO_FUNC_PARAM = 'func';
4+
export const GNO_SEND_PARAM = '.send';
5+
export const GNO_MAX_DEPOSIT_PARAM = '.max_deposit';
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
import { GnoMessageInfo, parseGnoMessageInfo } from './gno-connect';
2+
3+
describe('parseGnoMessageInfo', () => {
4+
describe('URL parsing tests', () => {
5+
it('should correctly parse URL with standard path and no pkgpath parameter', () => {
6+
// Test URL (Case 1)
7+
const url = 'https://gno.land/r/gnoland/users/v1$help&func=Register&.send=1000000ugnot';
8+
9+
// Execute function
10+
const result = parseGnoMessageInfo(url);
11+
12+
// Expected result
13+
const expected: GnoMessageInfo = {
14+
packagePath: 'gno.land/r/gnoland/users/v1',
15+
functionName: 'Register',
16+
send: '1000000ugnot',
17+
maxDeposit: '',
18+
args: null,
19+
};
20+
21+
// Verify result
22+
expect(result).toEqual(expected);
23+
});
24+
25+
it('should correctly parse and decode URL with pkgpath parameter', () => {
26+
// Test URL (Case 2)
27+
const url =
28+
'https://gno.land/r/leon/hor$help&func=Upvote&pkgpath=gno.land%2Fr%2Fmorgan%2Fhome';
29+
30+
// Execute function
31+
const result = parseGnoMessageInfo(url);
32+
33+
// Expected result
34+
const expected: GnoMessageInfo = {
35+
packagePath: 'gno.land/r/leon/hor',
36+
functionName: 'Upvote',
37+
send: '',
38+
maxDeposit: '',
39+
args: [
40+
{
41+
index: 0,
42+
key: 'pkgpath',
43+
value: 'gno.land/r/morgan/home',
44+
},
45+
],
46+
};
47+
48+
// Verify result
49+
expect(result).toEqual(expected);
50+
});
51+
});
52+
53+
describe('Complete field population tests', () => {
54+
it('should correctly parse URL with all five fields populated', () => {
55+
// Test URL (with all fields populated)
56+
const url =
57+
'https://gno.land/r/demo/test$help&func=CompleteFunction&.send=500000ugnot&.max_deposit=1000000ugnot&arg1=value1&arg2=value2&arg3=value3';
58+
59+
// Execute function
60+
const result = parseGnoMessageInfo(url);
61+
62+
// Expected result
63+
const expected: GnoMessageInfo = {
64+
packagePath: 'gno.land/r/demo/test',
65+
functionName: 'CompleteFunction',
66+
send: '500000ugnot',
67+
maxDeposit: '1000000ugnot',
68+
args: [
69+
{ index: 0, key: 'arg1', value: 'value1' },
70+
{ index: 1, key: 'arg2', value: 'value2' },
71+
{ index: 2, key: 'arg3', value: 'value3' },
72+
],
73+
};
74+
75+
// Verify result
76+
expect(result).toEqual(expected);
77+
});
78+
79+
it('should correctly parse URL with all five fields populated using pkgpath parameter', () => {
80+
// Test URL (with all fields populated using pkgpath)
81+
const url =
82+
'https://gno.land/r/demo/placeholder$help&func=FullTest&pkgpath=gno.land%2Fr%2Ftest%2Fcomplete&.send=750000ugnot&.max_deposit=2000000ugnot&param1=test1&param2=test2';
83+
84+
// Execute function
85+
const result = parseGnoMessageInfo(url);
86+
87+
// Expected result
88+
const expected: GnoMessageInfo = {
89+
packagePath: 'gno.land/r/demo/placeholder',
90+
functionName: 'FullTest',
91+
send: '750000ugnot',
92+
maxDeposit: '2000000ugnot',
93+
args: [
94+
{ index: 0, key: 'pkgpath', value: 'gno.land/r/test/complete' },
95+
{ index: 1, key: 'param1', value: 'test1' },
96+
{ index: 2, key: 'param2', value: 'test2' },
97+
],
98+
};
99+
100+
// Verify result
101+
expect(result).toEqual(expected);
102+
});
103+
});
104+
105+
describe('URL encoding and decoding tests', () => {
106+
it('should correctly decode all URL-encoded arguments', () => {
107+
// Test URL with multiple encoded parameters
108+
const url =
109+
'https://gno.land/r/demo/test$help&func=TestFunction' +
110+
'&encoded1=Hello%20World' +
111+
'&encoded2=%3Cscript%3Ealert(1)%3C%2Fscript%3E' +
112+
'&encoded3=special%40%23%24%25%5E%26*()' +
113+
'&encoded4=path%2Fto%2Fresource' +
114+
'&encoded5=%E2%9C%93%20Unicode%20Check';
115+
116+
// Execute function
117+
const result = parseGnoMessageInfo(url);
118+
119+
// Expected result
120+
const expected: GnoMessageInfo = {
121+
packagePath: 'gno.land/r/demo/test',
122+
functionName: 'TestFunction',
123+
send: '',
124+
maxDeposit: '',
125+
args: [
126+
{ index: 0, key: 'encoded1', value: 'Hello World' },
127+
{ index: 1, key: 'encoded2', value: '<script>alert(1)</script>' },
128+
{ index: 2, key: 'encoded3', value: 'special@#$%^&*()' },
129+
{ index: 3, key: 'encoded4', value: 'path/to/resource' },
130+
{ index: 4, key: 'encoded5', value: '✓ Unicode Check' },
131+
],
132+
};
133+
134+
// Verify result
135+
expect(result).toEqual(expected);
136+
});
137+
138+
it('should correctly handle mixed encoded and non-encoded arguments', () => {
139+
// Test URL with a mix of encoded and non-encoded parameters
140+
const url =
141+
'https://gno.land/r/demo/test$help&func=MixedTest' +
142+
'&plain=simple text' +
143+
'&encoded=complex%20text%20with%20spaces' +
144+
'&.send=1000ugnot' +
145+
'&mixed=half%20encoded+half+plus';
146+
147+
// Execute function
148+
const result = parseGnoMessageInfo(url);
149+
150+
// Expected result
151+
const expected: GnoMessageInfo = {
152+
packagePath: 'gno.land/r/demo/test',
153+
functionName: 'MixedTest',
154+
send: '1000ugnot',
155+
maxDeposit: '',
156+
args: [
157+
{ index: 0, key: 'plain', value: 'simple text' },
158+
{ index: 1, key: 'encoded', value: 'complex text with spaces' },
159+
{ index: 2, key: 'mixed', value: 'half encoded+half+plus' },
160+
],
161+
};
162+
163+
// Verify result
164+
expect(result).toEqual(expected);
165+
});
166+
167+
it('should properly decode pkgpath and other special parameters', () => {
168+
// Test URL with encoded pkgpath and other special parameters
169+
const url =
170+
'https://gno.land/r/demo/test$help&func=SpecialTest' +
171+
'&pkgpath=gno.land%2Fr%2Ftest%2Fpackage%20with%20spaces' +
172+
'&url=https%3A%2F%2Fexample.com%2Fpath%3Fparam%3Dvalue' +
173+
'&json=%7B%22key%22%3A%22value%22%7D'; // {"key":"value"}
174+
175+
// Execute function
176+
const result = parseGnoMessageInfo(url);
177+
178+
// Expected result
179+
const expected: GnoMessageInfo = {
180+
packagePath: 'gno.land/r/demo/test',
181+
functionName: 'SpecialTest',
182+
send: '',
183+
maxDeposit: '',
184+
args: [
185+
{ index: 0, key: 'pkgpath', value: 'gno.land/r/test/package with spaces' },
186+
{ index: 1, key: 'url', value: 'https://example.com/path?param=value' },
187+
{ index: 2, key: 'json', value: '{"key":"value"}' },
188+
],
189+
};
190+
191+
// Verify result
192+
expect(result).toEqual(expected);
193+
});
194+
});
195+
196+
describe('Invalid URL tests', () => {
197+
it('should return null for URL without $help marker', () => {
198+
// Test URL (Invalid Case 1)
199+
const url = 'https://gno.land/r/demo/demo';
200+
const result = parseGnoMessageInfo(url);
201+
expect(result).toBeNull();
202+
});
203+
204+
it('should return null for URL without func parameter', () => {
205+
// Test URL (Invalid Case 2)
206+
const url = 'https://gno.land/r/demo/demo$help';
207+
const result = parseGnoMessageInfo(url);
208+
expect(result).toBeNull();
209+
});
210+
211+
it('should return null for URL without package path', () => {
212+
// Test URL (Invalid Case 3)
213+
const url = 'https://gno.land/$help&func=Test';
214+
const result = parseGnoMessageInfo(url);
215+
expect(result).toBeNull();
216+
});
217+
});
218+
219+
describe('For data verification', () => {
220+
it('data console.log', () => {
221+
const url =
222+
'https://gno.land/r/leon/hor$help&func=Upvote&pkgpath=gno.land%2Fr%2Fmorgan%2Fhome';
223+
const result = parseGnoMessageInfo(url);
224+
console.log(result, 'result');
225+
});
226+
});
227+
});

packages/adena-extension/src/inject/message/methods/gno-connect.ts

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ import {
44
GNO_PACKAGE_PREFIX,
55
GNO_RPC_META_TAG,
66
} from '@common/constants/metatag.constant';
7+
import {
8+
GNO_HELP_MARKER,
9+
GNO_FUNC_PARAM,
10+
GNO_SEND_PARAM,
11+
GNO_MAX_DEPOSIT_PARAM,
12+
} from '@common/constants/url.constant';
713
import { hasHttpProtocol } from '@common/provider/gno/utils';
814

915
export interface GnoConnectInfo {
@@ -89,7 +95,7 @@ export function parseGnoConnectInfo(): GnoConnectInfo | null {
8995
export function parseGnoMessageInfo(href: string): GnoMessageInfo | null {
9096
const url = new URL(href);
9197
const { pathname } = url;
92-
if (!pathname.includes('$help')) {
98+
if (!pathname.includes(GNO_HELP_MARKER)) {
9399
return null;
94100
}
95101

@@ -100,12 +106,8 @@ export function parseGnoMessageInfo(href: string): GnoMessageInfo | null {
100106
maxDeposit: '',
101107
args: null,
102108
};
103-
const splitter = '$help';
104-
if (!pathname.includes(splitter)) {
105-
return null;
106-
}
107109

108-
const [beforeHelp, afterHelp] = pathname.split('$help');
110+
const [beforeHelp, afterHelp] = pathname.split(GNO_HELP_MARKER);
109111
const packagePostfix = `${beforeHelp}`.replace(/^\/+/, '');
110112
if (packagePostfix === '') {
111113
return null;
@@ -115,7 +117,6 @@ export function parseGnoMessageInfo(href: string): GnoMessageInfo | null {
115117
const parts = queryPart.split('&');
116118

117119
const args: GnoArgumentInfo[] = [];
118-
119120
let argumentIndex = 0;
120121

121122
for (const p of parts) {
@@ -126,27 +127,24 @@ export function parseGnoMessageInfo(href: string): GnoMessageInfo | null {
126127

127128
const [key, value] = params;
128129

129-
if (key === 'func') {
130-
messageInfo.functionName = value || '';
131-
continue;
132-
}
133-
134-
if (key === '.send') {
135-
messageInfo.send = value || '';
136-
continue;
137-
}
138-
139-
if (key === '.max_deposit') {
140-
messageInfo.maxDeposit = value || '';
141-
continue;
130+
switch (key) {
131+
case GNO_FUNC_PARAM:
132+
messageInfo.functionName = value || '';
133+
continue;
134+
case GNO_SEND_PARAM:
135+
messageInfo.send = value || '';
136+
continue;
137+
case GNO_MAX_DEPOSIT_PARAM:
138+
messageInfo.maxDeposit = value || '';
139+
continue;
140+
default:
141+
args.push({
142+
index: argumentIndex,
143+
key,
144+
value: decodeURIComponent(value),
145+
});
142146
}
143147

144-
args.push({
145-
index: argumentIndex,
146-
key,
147-
value,
148-
});
149-
150148
argumentIndex++;
151149
}
152150

0 commit comments

Comments
 (0)