Skip to content

Commit 76f06e4

Browse files
committed
add new reusable workflow
1 parent 2e0d532 commit 76f06e4

File tree

7 files changed

+349
-0
lines changed

7 files changed

+349
-0
lines changed
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
name: 'Safe Transaction Proposer'
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
proposer-private-key:
7+
description: 'Private key of the proposer wallet'
8+
required: true
9+
type: string
10+
rpc-url:
11+
description: 'RPC URL for the blockchain network'
12+
required: true
13+
type: string
14+
safe-address:
15+
description: 'Address of the Safe contract'
16+
required: true
17+
type: string
18+
target-address:
19+
description: 'Target address for the transaction'
20+
required: true
21+
type: string
22+
safe-api-key:
23+
description: 'Safe API key for transaction service'
24+
required: true
25+
type: string
26+
chain-id:
27+
description: 'Chain ID of the blockchain network (default: 42161 for Arbitrum)'
28+
required: false
29+
default: '42161'
30+
type: string
31+
transaction-value:
32+
description: 'Value to send in the transaction (in wei, default: 0)'
33+
required: false
34+
default: '0'
35+
type: string
36+
transaction-data:
37+
description: 'Transaction data/calldata (default: 0x)'
38+
required: false
39+
default: '0x'
40+
type: string
41+
operation:
42+
description: 'Operation type (0 for Call, 1 for DelegateCall)'
43+
required: false
44+
default: '0'
45+
type: string
46+
outputs:
47+
safe-tx-hash:
48+
description: 'Hash of the Safe transaction'
49+
value: ${{ jobs.propose-transaction.outputs.safe-tx-hash }}
50+
transaction:
51+
description: 'Created transaction details'
52+
value: ${{ jobs.propose-transaction.outputs.transaction }}
53+
54+
jobs:
55+
propose-transaction:
56+
runs-on: ubuntu-latest
57+
outputs:
58+
safe-tx-hash: ${{ steps.safe-transaction.outputs.safe-tx-hash }}
59+
transaction: ${{ steps.safe-transaction.outputs.transaction }}
60+
61+
steps:
62+
- name: Checkout repository
63+
uses: actions/checkout@v4
64+
65+
- name: Setup Node.js
66+
uses: actions/setup-node@v4
67+
with:
68+
node-version: '20'
69+
cache: 'npm'
70+
71+
- name: Install dependencies
72+
run: npm ci
73+
74+
- name: Build action
75+
run: npm run build
76+
77+
- name: Propose Safe Transaction
78+
run: node dist/index.js
79+
env:
80+
INPUT_PROPOSER_PRIVATE_KEY: ${{ inputs.proposer-private-key }}
81+
INPUT_RPC_URL: ${{ inputs.rpc-url }}
82+
INPUT_SAFE_ADDRESS: ${{ inputs.safe-address }}
83+
INPUT_TARGET_ADDRESS: ${{ inputs.target-address }}
84+
INPUT_SAFE_API_KEY: ${{ inputs.safe-api-key }}
85+
INPUT_CHAIN_ID: ${{ inputs.chain-id }}
86+
INPUT_TRANSACTION_VALUE: ${{ inputs.transaction-value }}
87+
INPUT_TRANSACTION_DATA: ${{ inputs.transaction-data }}
88+
INPUT_OPERATION: ${{ inputs.operation }}
89+
id: safe-transaction

safe-transaction/.gitignore

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Dependencies
2+
node_modules/
3+
4+
# Build outputs
5+
dist/
6+
7+
# IDE
8+
.vscode/
9+
.idea/
10+
11+
# OS
12+
.DS_Store
13+
Thumbs.db
14+
15+
# Environment variables
16+
.env
17+
.env.local
18+
.env.*.local
19+
20+
# Logs
21+
logs/
22+
*.log

safe-transaction/CHANGELOG.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Changelog
2+
3+
Toutes les modifications notables de ce projet seront documentées dans ce fichier.
4+
5+
Le format est basé sur [Keep a Changelog](https://keepachangelog.com/fr/1.0.0/),
6+
et ce projet adhère au [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [1.0.0] - 2024-12-19
9+
10+
### Ajouté
11+
- ✅ Création et proposition de transactions Safe automatisées
12+
- 🔐 Support de la signature automatique avec clé privée
13+
- 📤 Intégration avec l'API Safe pour proposer les transactions
14+
- 🌐 Support multi-chaînes avec configuration du chain ID
15+
- ⚙️ Configuration flexible des paramètres de transaction
16+
- 📝 Documentation complète avec exemples d'utilisation
17+
- 🔒 Gestion sécurisée des secrets via GitHub Secrets
18+
19+
### Fonctionnalités
20+
- Création de transactions Safe avec support des opérations Call et DelegateCall
21+
- Signature automatique des transactions avec validation
22+
- Proposition automatique au service Safe avec vérification
23+
- Configuration flexible de la valeur et des données de transaction
24+
- Support des principales chaînes blockchain (Ethereum, Polygon, Arbitrum, etc.)
25+
- Logging détaillé pour le debug et le monitoring
26+
- Gestion d'erreurs robuste avec messages informatifs
27+
28+
### Sécurité
29+
- Utilisation des GitHub Secrets pour la gestion sécurisée des clés privées
30+
- Validation des entrées pour éviter les erreurs de configuration
31+
- Pas d'exposition des informations sensibles dans les logs
32+
- Support des meilleures pratiques de sécurité Safe
33+
34+
### Documentation
35+
- README complet avec exemples d'utilisation
36+
- Documentation des paramètres d'entrée et de sortie
37+
- Guide de configuration pour différents cas d'usage
38+
- Exemples de workflows pour différents scénarios
39+
- Conseils de sécurité et bonnes pratiques

safe-transaction/README.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Safe Transaction Proposer - Reusable Workflow
2+
3+
## Overview 🌟
4+
5+
This reusable GitHub Actions workflow automates the process of creating and proposing transactions to a Safe multi-signature wallet (Gnosis Safe). It handles the complete transaction lifecycle from creation to proposal submission, making it easy to integrate Safe transactions into your CI/CD pipeline.
6+
7+
## Workflow Inputs 🛠️
8+
9+
| **Input** | **Description** | **Required** | **Default** |
10+
| ------------------------ | ------------------------------------------------------------- | ------------ | ----------------------------------- |
11+
| **proposer-private-key** | Private key of the proposer wallet | Yes | - |
12+
| **rpc-url** | RPC URL for the blockchain network | Yes | - |
13+
| **safe-address** | Address of the Safe contract | Yes | - |
14+
| **target-address** | Target address for the transaction | Yes | - |
15+
| **safe-api-key** | Safe API key for transaction service | Yes | - |
16+
| **chain-id** | Chain ID of the blockchain network | No | `42161` (Arbitrum) |
17+
| **transaction-value** | Value to send in the transaction (in wei) | No | `0` |
18+
| **transaction-data** | Transaction data/calldata | No | `0x` |
19+
| **operation** | Operation type (0 for Call, 1 for DelegateCall) | No | `0` |
20+
21+
## Workflow Outputs 📤
22+
23+
| **Output** | **Description** |
24+
| ----------------- | ----------------------------------------- |
25+
| **safe-tx-hash** | Hash of the Safe transaction created |
26+
| **transaction** | Complete transaction details (JSON) |
27+
28+
## Secrets 🔐
29+
30+
| **Secret** | **Description** | **Required** |
31+
| ------------------------- | -------------------------------------------------- | ------------ |
32+
| **PROPOSER_PRIVATE_KEY** | Private key of the proposer wallet | Yes |
33+
| **SAFE_API_KEY** | Safe API key for transaction service | Yes |
34+
35+
## How to Use This Reusable Workflow 🔄
36+
37+
1. **Save the Workflow File**
38+
Place the `safe-transaction.yml` file in the `.github/workflows/` directory of your repository. 💾
39+
40+
2. **Call the Reusable Workflow**
41+
In another workflow file, invoke this reusable workflow like so:
42+
43+
```yaml
44+
name: Propose Safe Transaction
45+
on:
46+
workflow_dispatch:
47+
inputs:
48+
safe-address:
49+
description: 'Safe contract address'
50+
required: true
51+
target-address:
52+
description: 'Target contract address'
53+
required: true
54+
transaction-data:
55+
description: 'Transaction data (0x prefixed)'
56+
required: false
57+
default: '0x'
58+
59+
jobs:
60+
safe-transaction:
61+
uses: ./.github/workflows/safe-transaction.yml
62+
secrets:
63+
proposer-private-key: ${{ secrets.PROPOSER_PRIVATE_KEY }}
64+
rpc-url: ${{ vars.RPC_URL }}
65+
safe-api-key: ${{ secrets.SAFE_API_KEY }}
66+
with:
67+
safe-address: ${{ inputs.safe-address }}
68+
target-address: ${{ inputs.target-address }}
69+
transaction-data: ${{ inputs.transaction-data }}
70+
```
71+
72+
3. **Configure Secrets**
73+
Ensure that the required secrets are added to your repository's settings:
74+
- `PROPOSER_PRIVATE_KEY`: The private key of the wallet that will propose the transaction
75+
- `SAFE_API_KEY`: Your Safe API key for the transaction service
76+
77+
## Operation Types 🔧
78+
79+
- **`0`**: **Call** - Normal transaction execution
80+
- **`1`**: **DelegateCall** - Execution in the context of the Safe contract
81+
82+
## Security Considerations 🛡️
83+
84+
⚠️ **Important**: Never expose private keys in logs or code files. Always use GitHub Secrets to store sensitive information securely.

safe-transaction/package.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "safe-transaction-action",
3+
"version": "1.0.0",
4+
"description": "GitHub Action for proposing Safe transactions",
5+
"main": "dist/index.js",
6+
"scripts": {
7+
"build": "ncc build src/index.js -o dist",
8+
"prepare": "npm run build"
9+
},
10+
"keywords": [
11+
"github-actions",
12+
"safe",
13+
"gnosis-safe",
14+
"ethereum",
15+
"blockchain"
16+
],
17+
"dependencies": {
18+
"@actions/core": "^1.10.1",
19+
"@safe-global/api-kit": "^1.0.0",
20+
"@safe-global/protocol-kit": "^3.0.0",
21+
"@safe-global/types-kit": "^1.0.0",
22+
"ethers": "^6.11.0"
23+
},
24+
"devDependencies": {
25+
"@vercel/ncc": "^0.38.0"
26+
}
27+
}

safe-transaction/src/index.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
const core = require('@actions/core');
2+
const SafeApiKit = require('@safe-global/api-kit').default;
3+
const Safe = require('@safe-global/protocol-kit').default;
4+
const { OperationType } = require('@safe-global/types-kit');
5+
const { Wallet } = require('ethers');
6+
7+
async function run() {
8+
try {
9+
// Get inputs from environment variables
10+
const proposerPrivateKey = process.env.INPUT_PROPOSER_PRIVATE_KEY;
11+
const rpcUrl = process.env.INPUT_RPC_URL;
12+
const safeAddress = process.env.INPUT_SAFE_ADDRESS;
13+
const targetAddress = process.env.INPUT_TARGET_ADDRESS;
14+
const safeApiKey = process.env.INPUT_SAFE_API_KEY;
15+
const chainId = BigInt(process.env.INPUT_CHAIN_ID || '42161');
16+
const transactionValue = process.env.INPUT_TRANSACTION_VALUE || '0';
17+
const transactionData = process.env.INPUT_TRANSACTION_DATA || '0x';
18+
const operation = process.env.INPUT_OPERATION || '0';
19+
20+
core.info(`🚀 Starting Safe transaction creation...`);
21+
core.info(`📍 Safe Address: ${safeAddress}`);
22+
core.info(`🎯 Target Address: ${targetAddress}`);
23+
24+
// Initialize wallet
25+
const wallet = new Wallet(proposerPrivateKey);
26+
core.info(`👤 Proposer Address: ${wallet.address}`);
27+
28+
// Initialize API Kit
29+
const apiKit = new SafeApiKit({
30+
chainId: chainId,
31+
apiKey: safeApiKey
32+
});
33+
34+
// Initialize Protocol Kit
35+
const protocolKit = await Safe.init({
36+
provider: rpcUrl,
37+
signer: proposerPrivateKey,
38+
safeAddress: safeAddress
39+
});
40+
41+
// Create transaction
42+
const safeTransactionData = {
43+
to: targetAddress,
44+
value: transactionValue,
45+
data: transactionData,
46+
operation: parseInt(operation)
47+
};
48+
49+
core.info('📝 Creating Safe transaction...');
50+
const safeTransaction = await protocolKit.createTransaction({
51+
transactions: [safeTransactionData]
52+
});
53+
54+
const safeTxHash = await protocolKit.getTransactionHash(safeTransaction);
55+
const signature = await protocolKit.signHash(safeTxHash);
56+
57+
core.info(`🔐 Transaction signed with hash: ${safeTxHash}`);
58+
59+
// Propose transaction to the service
60+
await apiKit.proposeTransaction({
61+
safeAddress: safeAddress,
62+
safeTransactionData: safeTransaction.data,
63+
safeTxHash: safeTxHash,
64+
senderAddress: wallet.address,
65+
senderSignature: signature.data
66+
});
67+
68+
core.info('📤 Transaction proposed to Safe service');
69+
70+
// Get transaction details
71+
const transaction = await apiKit.getTransaction(safeTxHash);
72+
73+
// Set outputs
74+
core.setOutput('safe-tx-hash', safeTxHash);
75+
core.setOutput('transaction', JSON.stringify(transaction));
76+
77+
core.info(`✅ Transaction created successfully!`);
78+
core.info(`🔗 Transaction Hash: ${safeTxHash}`);
79+
core.info(`📋 Transaction Details: ${JSON.stringify(transaction, null, 2)}`);
80+
81+
} catch (error) {
82+
core.setFailed(`❌ Error creating Safe transaction: ${error.message}`);
83+
core.error(error.stack);
84+
}
85+
}
86+
87+
run();

safe-transaction/version.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0.0.0

0 commit comments

Comments
 (0)