Skip to content

Commit 6ad0ed8

Browse files
committed
Add support for sending bridge transactions
1 parent 87c6c6b commit 6ad0ed8

File tree

10 files changed

+466
-185
lines changed

10 files changed

+466
-185
lines changed

packages/mobile-app/app/(tabs)/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ export default function Balances() {
206206
appearance="ghost"
207207
accessoryLeft={BridgeIcon}
208208
style={styles.actionButton}
209+
onPress={() => router.push("/menu/debug/browser")}
209210
>
210211
Bridge
211212
</Button>

packages/mobile-app/app/menu/debug/browser.tsx

Lines changed: 123 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { WebView } from "react-native-webview";
2-
import Constants from "expo-constants";
32
import {
43
Button,
54
Modal,
@@ -13,20 +12,50 @@ import { Network } from "../../../data/constants";
1312
import { useRef, useState } from "react";
1413
import * as Uint8ArrayUtils from "../../../utils/uint8Array";
1514
import { useFacade } from "../../../data/facades";
15+
import { Output } from "@/data/facades/wallet/types";
16+
import SendTransactionModal from "@/components/browser/SendTransactionModal";
1617

1718
type Message = {
1819
id: number;
1920
type: string;
2021
data: Record<string, unknown> | null;
2122
};
2223

24+
const BRIDGE_URLS: Record<Network, string> = {
25+
[Network.MAINNET]: "https://bridge.ironfish.network/",
26+
[Network.TESTNET]: "https://testnet.bridge.ironfish.network/",
27+
};
28+
29+
export type Mint = {
30+
value: string;
31+
assetId?: string;
32+
name?: string;
33+
metadata?: string;
34+
};
35+
36+
export type Burn = {
37+
value: string;
38+
assetId: string;
39+
};
40+
41+
type GeneralTransactionData = {
42+
from: string;
43+
fee?: string;
44+
outputs?: Output[];
45+
mints?: Mint[];
46+
burns?: Burn[];
47+
};
48+
2349
class MessageHandler {
2450
activeAccount: { name: string; address: string } | null = null;
25-
activeAccountName = null;
2651
connectRequest: {
2752
resolve: (address: string | null) => void;
2853
reject: () => void;
2954
} | null = null;
55+
sendTransactionRequest: {
56+
resolve: (transaction: string) => void;
57+
reject: () => void;
58+
} | null = null;
3059

3160
async updateActiveAccount(
3261
activeAccount: { name: string; address: string } | null,
@@ -38,9 +67,20 @@ class MessageHandler {
3867
}
3968
}
4069

70+
async rejectSendTransactionRequest() {
71+
this.sendTransactionRequest?.reject();
72+
this.sendTransactionRequest = null;
73+
}
74+
75+
async resolveSendTransactionRequest(transaction: string) {
76+
this.sendTransactionRequest?.resolve(transaction);
77+
this.sendTransactionRequest = null;
78+
}
79+
4180
async handleMessage(
4281
data: string,
4382
showAccountModal: () => void,
83+
showSendTransactionModal: (data: GeneralTransactionData) => void,
4484
postMessage?: (data: string) => void,
4585
) {
4686
console.log(data);
@@ -106,6 +146,40 @@ class MessageHandler {
106146
},
107147
}),
108148
);
149+
} else if (message.type === "generalTransaction") {
150+
if (!message.data) {
151+
console.error("No data");
152+
return;
153+
}
154+
const data = message.data as GeneralTransactionData;
155+
if (data.from !== this.activeAccount?.address) {
156+
console.error("From address does not match active account");
157+
return;
158+
}
159+
if (data.mints && data.mints.length > 0) {
160+
console.error("Mints are not supported");
161+
return;
162+
}
163+
if (data.burns && data.burns.length > 0) {
164+
console.error("Burns are not supported");
165+
return;
166+
}
167+
168+
showSendTransactionModal({
169+
...data,
170+
// Replace the public address with the name of the account
171+
from: this.activeAccount.name,
172+
});
173+
const transaction = await new Promise<string>((resolve, reject) => {
174+
this.sendTransactionRequest = { resolve, reject };
175+
});
176+
postMessage?.(
177+
JSON.stringify({
178+
id: message.id,
179+
type: "generalTransaction",
180+
data: { transaction },
181+
}),
182+
);
109183
} else {
110184
console.error(`Invalid message type: ${message.type}`);
111185
}
@@ -117,9 +191,11 @@ export default function MenuDebugBrowser() {
117191
const messageHandler = useRef(new MessageHandler());
118192
const facade = useFacade();
119193

120-
const [modalVisible, setModalVisible] = useState(false);
194+
const [accountModalVisible, setAccountModalVisible] = useState(false);
195+
const [sendTransactionData, setSendTransactionData] =
196+
useState<GeneralTransactionData | null>(null);
121197
const accounts = facade.getAccounts.useQuery(undefined, {
122-
enabled: modalVisible,
198+
enabled: accountModalVisible,
123199
});
124200

125201
const js = `
@@ -140,6 +216,10 @@ export default function MenuDebugBrowser() {
140216
window.rpccalls[message.id].resolve(message.data.address);
141217
} else if (message.type === "getBalances") {
142218
window.rpccalls[message.id].resolve(message.data.balances);
219+
} else if (message.type === "generalTransaction") {
220+
window.rpccalls[message.id].resolve(message.data.transaction);
221+
} else if (message.type === "error") {
222+
window.rpccalls[message.id].reject(message.data.error);
143223
}
144224
});
145225
@@ -187,11 +267,27 @@ export default function MenuDebugBrowser() {
187267
console.log(result);
188268
return result;
189269
}
270+
async generalTransaction(e) {
271+
if (!this.#address) {
272+
throw new Error("Connect first");
273+
}
274+
const id = window.rpccounter++;
275+
window.ReactNativeWebView.postMessage(JSON.stringify({
276+
id,
277+
type: "generalTransaction",
278+
data: e,
279+
}));
280+
const result = await new Promise((resolve, reject) => {
281+
window.rpccalls[id] = { resolve, reject };
282+
});
283+
console.log(result);
284+
return result;
285+
}
190286
}
191287
192288
window.ironfish = new Proxy(new IronFishBridge(), {
193289
get: (obj, property, receiver) => {
194-
if (!property in obj) {
290+
if (!(property in obj)) {
195291
const message = \`ERROR: Please implement $\{property\} in IronFishBridge\`;
196292
console.error(message);
197293
return;
@@ -212,16 +308,16 @@ export default function MenuDebugBrowser() {
212308
<View style={styles.container}>
213309
<Modal
214310
animationType="slide"
215-
visible={modalVisible}
311+
visible={accountModalVisible}
216312
onRequestClose={() => {
217-
setModalVisible(false);
313+
messageHandler.current.updateActiveAccount(null);
314+
setAccountModalVisible(false);
218315
}}
219316
>
220317
<SafeAreaView>
221318
<View style={{ paddingTop: 40, paddingHorizontal: 4 }}>
222319
<Text style={{ fontSize: 20, textAlign: "center" }}>
223-
This website would like to connect to your wallet. Choose an
224-
account to connect, or click Cancel.
320+
This website would like to connect to your wallet.
225321
</Text>
226322
<Text style={{ textAlign: "center" }}>
227323
Choose an account to connect, or click Cancel.
@@ -234,30 +330,44 @@ export default function MenuDebugBrowser() {
234330
name: a.name,
235331
address: a.publicAddress,
236332
});
237-
setModalVisible(false);
333+
setAccountModalVisible(false);
238334
}}
239335
title={`${a.name} (${a.balances.iron.confirmed} $IRON)`}
240336
/>
241337
))}
242338
<Button
243339
onPress={() => {
244340
messageHandler.current.updateActiveAccount(null);
245-
setModalVisible(false);
341+
setAccountModalVisible(false);
246342
}}
247343
title="Cancel"
248344
/>
249345
</View>
250346
</SafeAreaView>
251347
</Modal>
348+
<SendTransactionModal
349+
sendTransactionData={sendTransactionData}
350+
cancel={() => {
351+
messageHandler.current.rejectSendTransactionRequest();
352+
setSendTransactionData(null);
353+
}}
354+
success={(hash) => {
355+
messageHandler.current.resolveSendTransactionRequest(hash);
356+
setSendTransactionData(null);
357+
}}
358+
/>
252359
<WebView
253-
source={{ uri: "https://testnet.bridge.ironfish.network" }}
360+
source={{ uri: BRIDGE_URLS[Network.TESTNET] }}
254361
ref={(r) => (webref.current = r)}
255362
injectedJavaScriptBeforeContentLoaded={js}
256363
onMessage={(event) => {
257364
messageHandler.current.handleMessage(
258365
event.nativeEvent.data,
259366
() => {
260-
setModalVisible(true);
367+
setAccountModalVisible(true);
368+
},
369+
(data) => {
370+
setSendTransactionData(data);
261371
},
262372
webref.current?.postMessage,
263373
);
@@ -271,6 +381,5 @@ export default function MenuDebugBrowser() {
271381
const styles = StyleSheet.create({
272382
container: {
273383
flex: 1,
274-
marginTop: Constants.statusBarHeight,
275384
},
276385
});

packages/mobile-app/app/menu/debug/oreowallet.tsx

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88
decodeAccount,
99
RawTransactionSerde,
1010
} from "@ironfish/sdk";
11-
import { wallet } from "../../../data/wallet/wallet";
1211

1312
export default function MenuDebugOreowallet() {
1413
const facade = useFacade();
@@ -105,31 +104,7 @@ export default function MenuDebugOreowallet() {
105104
Buffer.from(result.transaction, "hex"),
106105
);
107106

108-
const txnResult = await wallet.sendTransactionWithSpends(
109-
Network.MAINNET,
110-
account.data.name,
111-
txn.version,
112-
txn.fee.toString(),
113-
txn.outputs.map(({ note }) => ({
114-
amount: note.value().toString(),
115-
assetId: note.assetId().toString("hex"),
116-
publicAddress: note.owner(),
117-
memoHex: note.memo().toString("hex"),
118-
})),
119-
txn.spends.map(({ note, witness }) => ({
120-
note: note.serialize().toString("hex"),
121-
witnessTreeSize: witness.treeSize().toString(),
122-
witnessRootHash: witness
123-
.serializeRootHash()
124-
.toString("hex"),
125-
witnessAuthPath: witness.authPath().map((authPath) => ({
126-
hashOfSibling: authPath.hashOfSibling().toString("hex"),
127-
side: authPath.side(),
128-
})),
129-
})),
130-
);
131-
132-
console.log(JSON.stringify(txnResult));
107+
console.log(JSON.stringify(txn));
133108
}}
134109
title="Create Transaction"
135110
/>

0 commit comments

Comments
 (0)